[{"data":1,"prerenderedAt":2188},["ShallowReactive",2],{"blog-\u002Fblog\u002Fjava-records":3},{"id":4,"title":5,"body":6,"description":2175,"difficulty":2176,"extension":2177,"framework":2178,"frameworkSlug":23,"meta":2179,"navigation":82,"order":32,"path":2180,"qaPath":2181,"seo":2182,"stem":2183,"subtopic":2113,"topic":2184,"topicSlug":2185,"updated":2186,"__hash__":2187},"blog\u002Fblog\u002Fjava-records.md","Java Records Explained — Immutable Data Classes Without the Boilerplate",{"type":7,"value":8,"toc":2161},"minimark",[9,14,18,367,375,399,402,406,413,457,574,578,589,682,685,741,747,751,765,849,856,904,915,919,930,944,951,972,1170,1174,1185,1271,1280,1292,1296,1303,1401,1411,1415,1418,1518,1521,1525,1541,1654,1662,1666,1669,1844,1851,1855,1858,2082,2099,2105,2109,2157],[10,11,13],"h2",{"id":12},"the-boilerplate-problem-records-solve","The boilerplate problem records solve",[15,16,17],"p",{},"Before Java 16, a plain data-holding class required dozens of lines of repetitive code:",[19,20,25],"pre",{"className":21,"code":22,"language":23,"meta":24,"style":24},"language-java shiki shiki-themes github-light github-dark","public final class Point {\n    private final int x;\n    private final int y;\n\n    public Point(int x, int y) { this.x = x; this.y = y; }\n    public int x() { return x; }\n    public int y() { return y; }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (!(o instanceof Point other)) return false;\n        return x == other.x && y == other.y;\n    }\n    @Override public int hashCode() { return Objects.hash(x, y); }\n    @Override public String toString() { return \"Point[x=\" + x + \", y=\" + y + \"]\"; }\n}\n","java","",[26,27,28,51,65,77,84,136,154,169,174,183,203,228,254,280,286,315,361],"code",{"__ignoreMap":24},[29,30,33,37,40,43,47],"span",{"class":31,"line":32},"line",1,[29,34,36],{"class":35},"szBVR","public",[29,38,39],{"class":35}," final",[29,41,42],{"class":35}," class",[29,44,46],{"class":45},"sScJk"," Point",[29,48,50],{"class":49},"sVt8B"," {\n",[29,52,54,57,59,62],{"class":31,"line":53},2,[29,55,56],{"class":35},"    private",[29,58,39],{"class":35},[29,60,61],{"class":35}," int",[29,63,64],{"class":49}," x;\n",[29,66,68,70,72,74],{"class":31,"line":67},3,[29,69,56],{"class":35},[29,71,39],{"class":35},[29,73,61],{"class":35},[29,75,76],{"class":49}," y;\n",[29,78,80],{"class":31,"line":79},4,[29,81,83],{"emptyLinePlaceholder":82},true,"\n",[29,85,87,90,92,95,98,102,105,107,110,113,117,120,123,126,128,131,133],{"class":31,"line":86},5,[29,88,89],{"class":35},"    public",[29,91,46],{"class":45},[29,93,94],{"class":49},"(",[29,96,97],{"class":35},"int",[29,99,101],{"class":100},"s4XuR"," x",[29,103,104],{"class":49},", ",[29,106,97],{"class":35},[29,108,109],{"class":100}," y",[29,111,112],{"class":49},") { ",[29,114,116],{"class":115},"sj4cs","this",[29,118,119],{"class":49},".x ",[29,121,122],{"class":35},"=",[29,124,125],{"class":49}," x; ",[29,127,116],{"class":115},[29,129,130],{"class":49},".y ",[29,132,122],{"class":35},[29,134,135],{"class":49}," y; }\n",[29,137,139,141,143,145,148,151],{"class":31,"line":138},6,[29,140,89],{"class":35},[29,142,61],{"class":35},[29,144,101],{"class":45},[29,146,147],{"class":49},"() { ",[29,149,150],{"class":35},"return",[29,152,153],{"class":49}," x; }\n",[29,155,157,159,161,163,165,167],{"class":31,"line":156},7,[29,158,89],{"class":35},[29,160,61],{"class":35},[29,162,109],{"class":45},[29,164,147],{"class":49},[29,166,150],{"class":35},[29,168,135],{"class":49},[29,170,172],{"class":31,"line":171},8,[29,173,83],{"emptyLinePlaceholder":82},[29,175,177,180],{"class":31,"line":176},9,[29,178,179],{"class":49},"    @",[29,181,182],{"class":35},"Override\n",[29,184,186,188,191,194,197,200],{"class":31,"line":185},10,[29,187,89],{"class":35},[29,189,190],{"class":35}," boolean",[29,192,193],{"class":45}," equals",[29,195,196],{"class":49},"(Object ",[29,198,199],{"class":100},"o",[29,201,202],{"class":49},") {\n",[29,204,206,209,212,214,217,220,222,225],{"class":31,"line":205},11,[29,207,208],{"class":35},"        if",[29,210,211],{"class":49}," (",[29,213,116],{"class":115},[29,215,216],{"class":35}," ==",[29,218,219],{"class":49}," o) ",[29,221,150],{"class":35},[29,223,224],{"class":115}," true",[29,226,227],{"class":49},";\n",[29,229,231,233,235,238,241,244,247,249,252],{"class":31,"line":230},12,[29,232,208],{"class":35},[29,234,211],{"class":49},[29,236,237],{"class":35},"!",[29,239,240],{"class":49},"(o ",[29,242,243],{"class":35},"instanceof",[29,245,246],{"class":49}," Point other)) ",[29,248,150],{"class":35},[29,250,251],{"class":115}," false",[29,253,227],{"class":49},[29,255,257,260,263,266,269,272,275,277],{"class":31,"line":256},13,[29,258,259],{"class":35},"        return",[29,261,262],{"class":49}," x ",[29,264,265],{"class":35},"==",[29,267,268],{"class":49}," other.x ",[29,270,271],{"class":35},"&&",[29,273,274],{"class":49}," y ",[29,276,265],{"class":35},[29,278,279],{"class":49}," other.y;\n",[29,281,283],{"class":31,"line":282},14,[29,284,285],{"class":49},"    }\n",[29,287,289,291,294,297,299,302,304,306,309,312],{"class":31,"line":288},15,[29,290,179],{"class":49},[29,292,293],{"class":35},"Override",[29,295,296],{"class":35}," public",[29,298,61],{"class":35},[29,300,301],{"class":45}," hashCode",[29,303,147],{"class":49},[29,305,150],{"class":35},[29,307,308],{"class":49}," Objects.",[29,310,311],{"class":45},"hash",[29,313,314],{"class":49},"(x, y); }\n",[29,316,318,320,322,324,327,330,332,334,338,341,343,346,349,351,353,355,358],{"class":31,"line":317},16,[29,319,179],{"class":49},[29,321,293],{"class":35},[29,323,296],{"class":35},[29,325,326],{"class":49}," String ",[29,328,329],{"class":45},"toString",[29,331,147],{"class":49},[29,333,150],{"class":35},[29,335,337],{"class":336},"sZZnC"," \"Point[x=\"",[29,339,340],{"class":35}," +",[29,342,262],{"class":49},[29,344,345],{"class":35},"+",[29,347,348],{"class":336}," \", y=\"",[29,350,340],{"class":35},[29,352,274],{"class":49},[29,354,345],{"class":35},[29,356,357],{"class":336}," \"]\"",[29,359,360],{"class":49},"; }\n",[29,362,364],{"class":31,"line":363},17,[29,365,366],{"class":49},"}\n",[15,368,369,370,374],{},"With records (finalized in ",[371,372,373],"strong",{},"Java 16",", JEP 395):",[19,376,378],{"className":21,"code":377,"language":23,"meta":24,"style":24},"record Point(int x, int y) {}\n",[26,379,380],{"__ignoreMap":24},[29,381,382,385,387,389,391,394,396],{"class":31,"line":32},[29,383,384],{"class":35},"record",[29,386,46],{"class":45},[29,388,94],{"class":49},[29,390,97],{"class":35},[29,392,393],{"class":49}," x, ",[29,395,97],{"class":35},[29,397,398],{"class":49}," y) {}\n",[15,400,401],{},"One line. The compiler generates everything above automatically.",[10,403,405],{"id":404},"what-the-compiler-generates","What the compiler generates",[15,407,408,409,412],{},"The identifiers in the record header are ",[371,410,411],{},"components",". For each component the compiler\ncreates:",[414,415,416,424,438,444],"ul",{},[417,418,419,420,423],"li",{},"A ",[26,421,422],{},"private final"," field.",[417,425,419,426,429,430,433,434,437],{},[371,427,428],{},"public accessor method"," named after the component (",[26,431,432],{},"x()",", not ",[26,435,436],{},"getX()",").",[417,439,419,440,443],{},[371,441,442],{},"canonical constructor"," that accepts all components.",[417,445,446,104,449,452,453,456],{},[26,447,448],{},"equals()",[26,450,451],{},"hashCode()",", and ",[26,454,455],{},"toString()"," based on all components.",[19,458,460],{"className":21,"code":459,"language":23,"meta":24,"style":24},"record Person(String name, int age) {}\n\nPerson p = new Person(\"Alice\", 30);\np.name();       \u002F\u002F \"Alice\"\np.age();        \u002F\u002F 30\np.toString();   \u002F\u002F \"Person[name=Alice, age=30]\"\np.equals(new Person(\"Alice\", 30)); \u002F\u002F true\n",[26,461,462,477,481,506,521,534,546],{"__ignoreMap":24},[29,463,464,466,469,472,474],{"class":31,"line":32},[29,465,384],{"class":35},[29,467,468],{"class":45}," Person",[29,470,471],{"class":49},"(String name, ",[29,473,97],{"class":35},[29,475,476],{"class":49}," age) {}\n",[29,478,479],{"class":31,"line":53},[29,480,83],{"emptyLinePlaceholder":82},[29,482,483,486,488,491,493,495,498,500,503],{"class":31,"line":67},[29,484,485],{"class":49},"Person p ",[29,487,122],{"class":35},[29,489,490],{"class":35}," new",[29,492,468],{"class":45},[29,494,94],{"class":49},[29,496,497],{"class":336},"\"Alice\"",[29,499,104],{"class":49},[29,501,502],{"class":115},"30",[29,504,505],{"class":49},");\n",[29,507,508,511,514,517],{"class":31,"line":79},[29,509,510],{"class":49},"p.",[29,512,513],{"class":45},"name",[29,515,516],{"class":49},"();       ",[29,518,520],{"class":519},"sJ8bj","\u002F\u002F \"Alice\"\n",[29,522,523,525,528,531],{"class":31,"line":86},[29,524,510],{"class":49},[29,526,527],{"class":45},"age",[29,529,530],{"class":49},"();        ",[29,532,533],{"class":519},"\u002F\u002F 30\n",[29,535,536,538,540,543],{"class":31,"line":138},[29,537,510],{"class":49},[29,539,329],{"class":45},[29,541,542],{"class":49},"();   ",[29,544,545],{"class":519},"\u002F\u002F \"Person[name=Alice, age=30]\"\n",[29,547,548,550,553,555,558,560,562,564,566,568,571],{"class":31,"line":156},[29,549,510],{"class":49},[29,551,552],{"class":45},"equals",[29,554,94],{"class":49},[29,556,557],{"class":35},"new",[29,559,468],{"class":45},[29,561,94],{"class":49},[29,563,497],{"class":336},[29,565,104],{"class":49},[29,567,502],{"class":115},[29,569,570],{"class":49},")); ",[29,572,573],{"class":519},"\u002F\u002F true\n",[10,575,577],{"id":576},"immutability-shallow-not-deep","Immutability — shallow, not deep",[15,579,580,581,583,584,588],{},"Record fields are ",[26,582,422],{},", so a reference to a component cannot be reassigned\nafter construction. But if a component's type is mutable, the ",[585,586,587],"em",{},"object it refers to"," can\nstill change:",[19,590,592],{"className":21,"code":591,"language":23,"meta":24,"style":24},"record Scores(List\u003CInteger> values) {}\n\nvar s = new Scores(new ArrayList\u003C>(List.of(1, 2)));\ns.values().add(99); \u002F\u002F mutates the list — perfectly legal\n",[26,593,594,616,620,657],{"__ignoreMap":24},[29,595,596,598,601,604,607,610,613],{"class":31,"line":32},[29,597,384],{"class":35},[29,599,600],{"class":45}," Scores",[29,602,603],{"class":49},"(List",[29,605,606],{"class":35},"\u003C",[29,608,609],{"class":49},"Integer",[29,611,612],{"class":35},">",[29,614,615],{"class":49}," values) {}\n",[29,617,618],{"class":31,"line":53},[29,619,83],{"emptyLinePlaceholder":82},[29,621,622,625,628,630,632,634,636,638,641,644,646,649,651,654],{"class":31,"line":67},[29,623,624],{"class":35},"var",[29,626,627],{"class":49}," s ",[29,629,122],{"class":35},[29,631,490],{"class":35},[29,633,600],{"class":45},[29,635,94],{"class":49},[29,637,557],{"class":35},[29,639,640],{"class":49}," ArrayList\u003C>(List.",[29,642,643],{"class":45},"of",[29,645,94],{"class":49},[29,647,648],{"class":115},"1",[29,650,104],{"class":49},[29,652,653],{"class":115},"2",[29,655,656],{"class":49},")));\n",[29,658,659,662,665,668,671,673,676,679],{"class":31,"line":79},[29,660,661],{"class":49},"s.",[29,663,664],{"class":45},"values",[29,666,667],{"class":49},"().",[29,669,670],{"class":45},"add",[29,672,94],{"class":49},[29,674,675],{"class":115},"99",[29,677,678],{"class":49},"); ",[29,680,681],{"class":519},"\u002F\u002F mutates the list — perfectly legal\n",[15,683,684],{},"For true deep immutability, defensively copy mutable components in the canonical\nconstructor:",[19,686,688],{"className":21,"code":687,"language":23,"meta":24,"style":24},"record Scores(List\u003CInteger> values) {\n    Scores {\n        values = List.copyOf(values); \u002F\u002F unmodifiable snapshot\n    }\n}\n",[26,689,690,707,714,733,737],{"__ignoreMap":24},[29,691,692,694,696,698,700,702,704],{"class":31,"line":32},[29,693,384],{"class":35},[29,695,600],{"class":45},[29,697,603],{"class":49},[29,699,606],{"class":35},[29,701,609],{"class":49},[29,703,612],{"class":35},[29,705,706],{"class":49}," values) {\n",[29,708,709,712],{"class":31,"line":53},[29,710,711],{"class":45},"    Scores",[29,713,50],{"class":49},[29,715,716,719,721,724,727,730],{"class":31,"line":67},[29,717,718],{"class":49},"        values ",[29,720,122],{"class":35},[29,722,723],{"class":49}," List.",[29,725,726],{"class":45},"copyOf",[29,728,729],{"class":49},"(values); ",[29,731,732],{"class":519},"\u002F\u002F unmodifiable snapshot\n",[29,734,735],{"class":31,"line":79},[29,736,285],{"class":49},[29,738,739],{"class":31,"line":86},[29,740,366],{"class":49},[15,742,743,746],{},[371,744,745],{},"Rule of thumb:"," records give you shallow immutability by default. Wrap or copy mutable\ncomponents yourself when you need a value object that can't be modified from outside.",[10,748,750],{"id":749},"the-canonical-and-compact-constructor","The canonical and compact constructor",[15,752,753,754,756,757,760,761,764],{},"The ",[371,755,442],{}," has the same parameter list as the header. You can override\nit for validation or normalisation. The ",[371,758,759],{},"compact form"," (no parameter list, no explicit\nfield assignments) is the preferred style — the compiler appends ",[26,762,763],{},"this.x = x; this.y = y;","\nautomatically after your body:",[19,766,768],{"className":21,"code":767,"language":23,"meta":24,"style":24},"record Range(int min, int max) {\n    Range {   \u002F\u002F compact canonical constructor\n        if (min > max)\n            throw new IllegalArgumentException(\"min=%d > max=%d\".formatted(min, max));\n        \u002F\u002F compiler inserts: this.min = min; this.max = max;\n    }\n}\n",[26,769,770,789,800,812,836,841,845],{"__ignoreMap":24},[29,771,772,774,777,779,781,784,786],{"class":31,"line":32},[29,773,384],{"class":35},[29,775,776],{"class":45}," Range",[29,778,94],{"class":49},[29,780,97],{"class":35},[29,782,783],{"class":49}," min, ",[29,785,97],{"class":35},[29,787,788],{"class":49}," max) {\n",[29,790,791,794,797],{"class":31,"line":53},[29,792,793],{"class":45},"    Range",[29,795,796],{"class":49}," {   ",[29,798,799],{"class":519},"\u002F\u002F compact canonical constructor\n",[29,801,802,804,807,809],{"class":31,"line":67},[29,803,208],{"class":35},[29,805,806],{"class":49}," (min ",[29,808,612],{"class":35},[29,810,811],{"class":49}," max)\n",[29,813,814,817,819,822,824,827,830,833],{"class":31,"line":79},[29,815,816],{"class":35},"            throw",[29,818,490],{"class":35},[29,820,821],{"class":45}," IllegalArgumentException",[29,823,94],{"class":49},[29,825,826],{"class":336},"\"min=%d > max=%d\"",[29,828,829],{"class":49},".",[29,831,832],{"class":45},"formatted",[29,834,835],{"class":49},"(min, max));\n",[29,837,838],{"class":31,"line":86},[29,839,840],{"class":519},"        \u002F\u002F compiler inserts: this.min = min; this.max = max;\n",[29,842,843],{"class":31,"line":138},[29,844,285],{"class":49},[29,846,847],{"class":31,"line":156},[29,848,366],{"class":49},[15,850,851,852,855],{},"You can also add ",[371,853,854],{},"alternative constructors"," as long as they delegate to the canonical\none as their first statement:",[19,857,859],{"className":21,"code":858,"language":23,"meta":24,"style":24},"record Point(double x, double y) {\n    Point() { this(0.0, 0.0); }\n}\n",[26,860,861,879,900],{"__ignoreMap":24},[29,862,863,865,867,869,872,874,876],{"class":31,"line":32},[29,864,384],{"class":35},[29,866,46],{"class":45},[29,868,94],{"class":49},[29,870,871],{"class":35},"double",[29,873,393],{"class":49},[29,875,871],{"class":35},[29,877,878],{"class":49}," y) {\n",[29,880,881,884,886,888,890,893,895,897],{"class":31,"line":53},[29,882,883],{"class":45},"    Point",[29,885,147],{"class":49},[29,887,116],{"class":115},[29,889,94],{"class":49},[29,891,892],{"class":115},"0.0",[29,894,104],{"class":49},[29,896,892],{"class":115},[29,898,899],{"class":49},"); }\n",[29,901,902],{"class":31,"line":67},[29,903,366],{"class":49},[15,905,906,907,910,911,914],{},"A key safety property: ",[371,908,909],{},"deserialization always invokes the canonical constructor",", so\nvalidation in the compact constructor is enforced even when a ",[26,912,913],{},"Point"," is deserialized from\nbytes — unlike regular classes where Java deserialization bypasses constructors entirely.",[10,916,918],{"id":917},"records-vs-regular-classes-restrictions","Records vs regular classes — restrictions",[15,920,921,922,925,926,929],{},"Records are implicitly ",[26,923,924],{},"final"," (cannot be subclassed) and implicitly extend\n",[26,927,928],{},"java.lang.Record",". Beyond the generated members, you cannot add:",[414,931,932,935,938],{},[417,933,934],{},"Additional non-static instance fields (only the components exist).",[417,936,937],{},"Superclass declarations (records can't extend any class).",[417,939,940,943],{},[26,941,942],{},"abstract"," modifier.",[15,945,946,947,950],{},"You ",[585,948,949],{},"can"," add:",[414,952,953,956,959,969],{},[417,954,955],{},"Static fields and static methods.",[417,957,958],{},"Additional instance methods.",[417,960,961,962,104,965,968],{},"Interface implementations (",[26,963,964],{},"implements Comparable\u003CT>",[26,966,967],{},"implements Serializable",", etc.).",[417,970,971],{},"Annotations on components.",[19,973,975],{"className":21,"code":974,"language":23,"meta":24,"style":24},"record Temperature(double celsius) implements Comparable\u003CTemperature> {\n    static final double ABS_ZERO = -273.15;\n\n    static Temperature ofFahrenheit(double f) {\n        return new Temperature((f - 32) * 5.0 \u002F 9.0);\n    }\n\n    double toFahrenheit() { return celsius * 9.0 \u002F 5.0 + 32; }\n\n    @Override\n    public int compareTo(Temperature other) {\n        return Double.compare(celsius, other.celsius);\n    }\n}\n",[26,976,977,1005,1028,1032,1051,1085,1089,1093,1122,1126,1132,1149,1162,1166],{"__ignoreMap":24},[29,978,979,981,984,986,988,991,994,997,999,1002],{"class":31,"line":32},[29,980,384],{"class":35},[29,982,983],{"class":45}," Temperature",[29,985,94],{"class":49},[29,987,871],{"class":35},[29,989,990],{"class":49}," celsius) ",[29,992,993],{"class":35},"implements",[29,995,996],{"class":45}," Comparable",[29,998,606],{"class":49},[29,1000,1001],{"class":35},"Temperature",[29,1003,1004],{"class":49},"> {\n",[29,1006,1007,1010,1012,1015,1018,1020,1023,1026],{"class":31,"line":53},[29,1008,1009],{"class":35},"    static",[29,1011,39],{"class":35},[29,1013,1014],{"class":35}," double",[29,1016,1017],{"class":49}," ABS_ZERO ",[29,1019,122],{"class":35},[29,1021,1022],{"class":35}," -",[29,1024,1025],{"class":115},"273.15",[29,1027,227],{"class":49},[29,1029,1030],{"class":31,"line":67},[29,1031,83],{"emptyLinePlaceholder":82},[29,1033,1034,1036,1039,1042,1044,1046,1049],{"class":31,"line":79},[29,1035,1009],{"class":35},[29,1037,1038],{"class":49}," Temperature ",[29,1040,1041],{"class":45},"ofFahrenheit",[29,1043,94],{"class":49},[29,1045,871],{"class":35},[29,1047,1048],{"class":100}," f",[29,1050,202],{"class":49},[29,1052,1053,1055,1057,1059,1062,1065,1068,1071,1074,1077,1080,1083],{"class":31,"line":86},[29,1054,259],{"class":35},[29,1056,490],{"class":35},[29,1058,983],{"class":45},[29,1060,1061],{"class":49},"((f ",[29,1063,1064],{"class":35},"-",[29,1066,1067],{"class":115}," 32",[29,1069,1070],{"class":49},") ",[29,1072,1073],{"class":35},"*",[29,1075,1076],{"class":115}," 5.0",[29,1078,1079],{"class":35}," \u002F",[29,1081,1082],{"class":115}," 9.0",[29,1084,505],{"class":49},[29,1086,1087],{"class":31,"line":138},[29,1088,285],{"class":49},[29,1090,1091],{"class":31,"line":156},[29,1092,83],{"emptyLinePlaceholder":82},[29,1094,1095,1098,1101,1103,1105,1108,1110,1112,1114,1116,1118,1120],{"class":31,"line":171},[29,1096,1097],{"class":35},"    double",[29,1099,1100],{"class":45}," toFahrenheit",[29,1102,147],{"class":49},[29,1104,150],{"class":35},[29,1106,1107],{"class":49}," celsius ",[29,1109,1073],{"class":35},[29,1111,1082],{"class":115},[29,1113,1079],{"class":35},[29,1115,1076],{"class":115},[29,1117,340],{"class":35},[29,1119,1067],{"class":115},[29,1121,360],{"class":49},[29,1123,1124],{"class":31,"line":176},[29,1125,83],{"emptyLinePlaceholder":82},[29,1127,1128,1130],{"class":31,"line":185},[29,1129,179],{"class":49},[29,1131,182],{"class":35},[29,1133,1134,1136,1138,1141,1144,1147],{"class":31,"line":205},[29,1135,89],{"class":35},[29,1137,61],{"class":35},[29,1139,1140],{"class":45}," compareTo",[29,1142,1143],{"class":49},"(Temperature ",[29,1145,1146],{"class":100},"other",[29,1148,202],{"class":49},[29,1150,1151,1153,1156,1159],{"class":31,"line":230},[29,1152,259],{"class":35},[29,1154,1155],{"class":49}," Double.",[29,1157,1158],{"class":45},"compare",[29,1160,1161],{"class":49},"(celsius, other.celsius);\n",[29,1163,1164],{"class":31,"line":256},[29,1165,285],{"class":49},[29,1167,1168],{"class":31,"line":282},[29,1169,366],{"class":49},[10,1171,1173],{"id":1172},"records-vs-lombok","Records vs Lombok",[15,1175,1176,1177,1180,1181,1184],{},"Lombok's ",[26,1178,1179],{},"@Value"," and ",[26,1182,1183],{},"@Data"," were the pre-records workaround. The key differences:",[1186,1187,1188,1206],"table",{},[1189,1190,1191],"thead",{},[1192,1193,1194,1198,1201],"tr",{},[1195,1196,1197],"th",{},"Aspect",[1195,1199,1200],{},"Java Record",[1195,1202,1203,1204],{},"Lombok ",[26,1205,1179],{},[1207,1208,1209,1224,1235,1246,1257],"tbody",{},[1192,1210,1211,1215,1219],{},[1212,1213,1214],"td",{},"Accessor style",[1212,1216,1217],{},[26,1218,432],{},[1212,1220,1221,1223],{},[26,1222,436],{}," (JavaBean)",[1192,1225,1226,1229,1232],{},[1212,1227,1228],{},"Inheritance",[1212,1230,1231],{},"Cannot extend or be subclassed",[1212,1233,1234],{},"Normal",[1192,1236,1237,1240,1243],{},[1212,1238,1239],{},"Build tool",[1212,1241,1242],{},"None — JDK feature",[1212,1244,1245],{},"Requires annotation processor",[1192,1247,1248,1251,1254],{},[1212,1249,1250],{},"Jackson compat",[1212,1252,1253],{},"Native since Jackson 2.12",[1212,1255,1256],{},"Works with any version",[1192,1258,1259,1262,1265],{},[1212,1260,1261],{},"Builders",[1212,1263,1264],{},"No built-in builder",[1212,1266,1267,1270],{},[26,1268,1269],{},"@Builder"," available",[15,1272,1273,1276,1277,1279],{},[371,1274,1275],{},"When to prefer records:"," new code on Java 16+, no need for ",[26,1278,436],{}," naming, no need\nfor mutable builders.",[15,1281,1282,1285,1286,1288,1289,1291],{},[371,1283,1284],{},"When to prefer Lombok:"," legacy codebase with frameworks expecting ",[26,1287,436],{},", need for\n",[26,1290,1269],{},", or targeting Java \u003C 16.",[10,1293,1295],{"id":1294},"jackson-integration","Jackson integration",[15,1297,1298,1299,1302],{},"Jackson supports records natively since ",[371,1300,1301],{},"2.12",". Serialization uses the component\naccessors; deserialization uses the canonical constructor:",[19,1304,1306],{"className":21,"code":1305,"language":23,"meta":24,"style":24},"record User(String name, int age) {}\n\nvar mapper = new ObjectMapper();\nString json = mapper.writeValueAsString(new User(\"Alice\", 30));\n\u002F\u002F {\"name\":\"Alice\",\"age\":30}\n\nUser u = mapper.readValue(json, User.class);\n\u002F\u002F User[name=Alice, age=30]\n",[26,1307,1308,1321,1325,1342,1372,1377,1381,1396],{"__ignoreMap":24},[29,1309,1310,1312,1315,1317,1319],{"class":31,"line":32},[29,1311,384],{"class":35},[29,1313,1314],{"class":45}," User",[29,1316,471],{"class":49},[29,1318,97],{"class":35},[29,1320,476],{"class":49},[29,1322,1323],{"class":31,"line":53},[29,1324,83],{"emptyLinePlaceholder":82},[29,1326,1327,1329,1332,1334,1336,1339],{"class":31,"line":67},[29,1328,624],{"class":35},[29,1330,1331],{"class":49}," mapper ",[29,1333,122],{"class":35},[29,1335,490],{"class":35},[29,1337,1338],{"class":45}," ObjectMapper",[29,1340,1341],{"class":49},"();\n",[29,1343,1344,1347,1349,1352,1355,1357,1359,1361,1363,1365,1367,1369],{"class":31,"line":79},[29,1345,1346],{"class":49},"String json ",[29,1348,122],{"class":35},[29,1350,1351],{"class":49}," mapper.",[29,1353,1354],{"class":45},"writeValueAsString",[29,1356,94],{"class":49},[29,1358,557],{"class":35},[29,1360,1314],{"class":45},[29,1362,94],{"class":49},[29,1364,497],{"class":336},[29,1366,104],{"class":49},[29,1368,502],{"class":115},[29,1370,1371],{"class":49},"));\n",[29,1373,1374],{"class":31,"line":86},[29,1375,1376],{"class":519},"\u002F\u002F {\"name\":\"Alice\",\"age\":30}\n",[29,1378,1379],{"class":31,"line":138},[29,1380,83],{"emptyLinePlaceholder":82},[29,1382,1383,1386,1388,1390,1393],{"class":31,"line":156},[29,1384,1385],{"class":49},"User u ",[29,1387,122],{"class":35},[29,1389,1351],{"class":49},[29,1391,1392],{"class":45},"readValue",[29,1394,1395],{"class":49},"(json, User.class);\n",[29,1397,1398],{"class":31,"line":171},[29,1399,1400],{"class":519},"\u002F\u002F User[name=Alice, age=30]\n",[15,1402,1403,1404,1407,1408,829],{},"For older Jackson, add ",[26,1405,1406],{},"-parameters"," to the compiler flags so parameter names are\npreserved in bytecode, and include ",[26,1409,1410],{},"jackson-module-parameter-names",[10,1412,1414],{"id":1413},"generic-records","Generic records",[15,1416,1417],{},"Type parameters work exactly as on regular classes:",[19,1419,1421],{"className":21,"code":1420,"language":23,"meta":24,"style":24},"record Pair\u003CA, B>(A first, B second) {}\nrecord Result\u003CT>(T value, String message) {}\n\nvar p = new Pair\u003CString, Integer>(\"hello\", 42);\np.first();  \u002F\u002F \"hello\"\np.second(); \u002F\u002F 42\n",[26,1422,1423,1439,1454,1458,1492,1505],{"__ignoreMap":24},[29,1424,1425,1428,1431,1433,1436],{"class":31,"line":32},[29,1426,1427],{"class":49},"record Pair\u003C",[29,1429,1430],{"class":35},"A",[29,1432,104],{"class":49},[29,1434,1435],{"class":35},"B",[29,1437,1438],{"class":49},">(A first, B second) {}\n",[29,1440,1441,1443,1446,1448,1451],{"class":31,"line":53},[29,1442,384],{"class":35},[29,1444,1445],{"class":45}," Result",[29,1447,606],{"class":49},[29,1449,1450],{"class":35},"T",[29,1452,1453],{"class":49},">(T value, String message) {}\n",[29,1455,1456],{"class":31,"line":67},[29,1457,83],{"emptyLinePlaceholder":82},[29,1459,1460,1462,1465,1467,1469,1472,1475,1477,1479,1482,1485,1487,1490],{"class":31,"line":79},[29,1461,624],{"class":35},[29,1463,1464],{"class":49}," p ",[29,1466,122],{"class":35},[29,1468,490],{"class":35},[29,1470,1471],{"class":49}," Pair\u003C",[29,1473,1474],{"class":35},"String",[29,1476,104],{"class":49},[29,1478,609],{"class":35},[29,1480,1481],{"class":49},">(",[29,1483,1484],{"class":336},"\"hello\"",[29,1486,104],{"class":49},[29,1488,1489],{"class":115},"42",[29,1491,505],{"class":49},[29,1493,1494,1496,1499,1502],{"class":31,"line":86},[29,1495,510],{"class":49},[29,1497,1498],{"class":45},"first",[29,1500,1501],{"class":49},"();  ",[29,1503,1504],{"class":519},"\u002F\u002F \"hello\"\n",[29,1506,1507,1509,1512,1515],{"class":31,"line":138},[29,1508,510],{"class":49},[29,1510,1511],{"class":45},"second",[29,1513,1514],{"class":49},"(); ",[29,1516,1517],{"class":519},"\u002F\u002F 42\n",[15,1519,1520],{},"Generic records replace generic tuple classes with a concise, self-documenting\ndeclaration.",[10,1522,1524],{"id":1523},"records-as-map-keys-and-set-elements","Records as Map keys and Set elements",[15,1526,1527,1528,1180,1530,1532,1533,1536,1537,1540],{},"The generated ",[26,1529,448],{},[26,1531,451],{}," are based on all components, making records\ncorrect ",[26,1534,1535],{},"HashMap","\u002F",[26,1538,1539],{},"HashSet"," keys immediately:",[19,1542,1544],{"className":21,"code":1543,"language":23,"meta":24,"style":24},"record Point(int x, int y) {}\n\nvar labels = new HashMap\u003CPoint, String>();\nlabels.put(new Point(0, 0), \"origin\");\nSystem.out.println(labels.get(new Point(0, 0))); \u002F\u002F \"origin\"\n",[26,1545,1546,1562,1566,1589,1620],{"__ignoreMap":24},[29,1547,1548,1550,1552,1554,1556,1558,1560],{"class":31,"line":32},[29,1549,384],{"class":35},[29,1551,46],{"class":45},[29,1553,94],{"class":49},[29,1555,97],{"class":35},[29,1557,393],{"class":49},[29,1559,97],{"class":35},[29,1561,398],{"class":49},[29,1563,1564],{"class":31,"line":53},[29,1565,83],{"emptyLinePlaceholder":82},[29,1567,1568,1570,1573,1575,1577,1580,1582,1584,1586],{"class":31,"line":67},[29,1569,624],{"class":35},[29,1571,1572],{"class":49}," labels ",[29,1574,122],{"class":35},[29,1576,490],{"class":35},[29,1578,1579],{"class":49}," HashMap\u003C",[29,1581,913],{"class":35},[29,1583,104],{"class":49},[29,1585,1474],{"class":35},[29,1587,1588],{"class":49},">();\n",[29,1590,1591,1594,1597,1599,1601,1603,1605,1608,1610,1612,1615,1618],{"class":31,"line":79},[29,1592,1593],{"class":49},"labels.",[29,1595,1596],{"class":45},"put",[29,1598,94],{"class":49},[29,1600,557],{"class":35},[29,1602,46],{"class":45},[29,1604,94],{"class":49},[29,1606,1607],{"class":115},"0",[29,1609,104],{"class":49},[29,1611,1607],{"class":115},[29,1613,1614],{"class":49},"), ",[29,1616,1617],{"class":336},"\"origin\"",[29,1619,505],{"class":49},[29,1621,1622,1625,1628,1631,1634,1636,1638,1640,1642,1644,1646,1648,1651],{"class":31,"line":86},[29,1623,1624],{"class":49},"System.out.",[29,1626,1627],{"class":45},"println",[29,1629,1630],{"class":49},"(labels.",[29,1632,1633],{"class":45},"get",[29,1635,94],{"class":49},[29,1637,557],{"class":35},[29,1639,46],{"class":45},[29,1641,94],{"class":49},[29,1643,1607],{"class":115},[29,1645,104],{"class":49},[29,1647,1607],{"class":115},[29,1649,1650],{"class":49},"))); ",[29,1652,1653],{"class":519},"\u002F\u002F \"origin\"\n",[15,1655,1656,1657,1536,1659,1661],{},"With a regular class you'd have to implement ",[26,1658,448],{},[26,1660,451],{}," manually or rely on\nIDE generation (which can drift from the fields over time).",[10,1663,1665],{"id":1664},"local-records","Local records",[15,1667,1668],{},"Records can be declared inside a method — scoped to that method, invisible outside:",[19,1670,1672],{"className":21,"code":1671,"language":23,"meta":24,"style":24},"List\u003CString> topEmails(List\u003CUser> users) {\n    record Ranked(User user, int score) {}  \u002F\u002F local record\n\n    return users.stream()\n        .map(u -> new Ranked(u, score(u)))\n        .sorted(Comparator.comparingInt(Ranked::score).reversed())\n        .limit(10)\n        .map(r -> r.user().email())\n        .toList();\n}\n",[26,1673,1674,1700,1719,1723,1737,1764,1792,1807,1831,1840],{"__ignoreMap":24},[29,1675,1676,1679,1681,1683,1685,1688,1690,1692,1695,1697],{"class":31,"line":32},[29,1677,1678],{"class":49},"List",[29,1680,606],{"class":35},[29,1682,1474],{"class":49},[29,1684,612],{"class":35},[29,1686,1687],{"class":45}," topEmails",[29,1689,603],{"class":49},[29,1691,606],{"class":35},[29,1693,1694],{"class":49},"User",[29,1696,612],{"class":35},[29,1698,1699],{"class":49}," users) {\n",[29,1701,1702,1705,1708,1711,1713,1716],{"class":31,"line":53},[29,1703,1704],{"class":35},"    record",[29,1706,1707],{"class":45}," Ranked",[29,1709,1710],{"class":49},"(User user, ",[29,1712,97],{"class":35},[29,1714,1715],{"class":49}," score) {}  ",[29,1717,1718],{"class":519},"\u002F\u002F local record\n",[29,1720,1721],{"class":31,"line":67},[29,1722,83],{"emptyLinePlaceholder":82},[29,1724,1725,1728,1731,1734],{"class":31,"line":79},[29,1726,1727],{"class":35},"    return",[29,1729,1730],{"class":49}," users.",[29,1732,1733],{"class":45},"stream",[29,1735,1736],{"class":49},"()\n",[29,1738,1739,1742,1745,1748,1751,1753,1755,1758,1761],{"class":31,"line":86},[29,1740,1741],{"class":49},"        .",[29,1743,1744],{"class":45},"map",[29,1746,1747],{"class":49},"(u ",[29,1749,1750],{"class":35},"->",[29,1752,490],{"class":35},[29,1754,1707],{"class":45},[29,1756,1757],{"class":49},"(u, ",[29,1759,1760],{"class":45},"score",[29,1762,1763],{"class":49},"(u)))\n",[29,1765,1766,1768,1771,1774,1777,1780,1783,1786,1789],{"class":31,"line":138},[29,1767,1741],{"class":49},[29,1769,1770],{"class":45},"sorted",[29,1772,1773],{"class":49},"(Comparator.",[29,1775,1776],{"class":45},"comparingInt",[29,1778,1779],{"class":49},"(Ranked",[29,1781,1782],{"class":35},"::",[29,1784,1785],{"class":49},"score).",[29,1787,1788],{"class":45},"reversed",[29,1790,1791],{"class":49},"())\n",[29,1793,1794,1796,1799,1801,1804],{"class":31,"line":156},[29,1795,1741],{"class":49},[29,1797,1798],{"class":45},"limit",[29,1800,94],{"class":49},[29,1802,1803],{"class":115},"10",[29,1805,1806],{"class":49},")\n",[29,1808,1809,1811,1813,1816,1818,1821,1824,1826,1829],{"class":31,"line":171},[29,1810,1741],{"class":49},[29,1812,1744],{"class":45},[29,1814,1815],{"class":49},"(r ",[29,1817,1750],{"class":35},[29,1819,1820],{"class":49}," r.",[29,1822,1823],{"class":45},"user",[29,1825,667],{"class":49},[29,1827,1828],{"class":45},"email",[29,1830,1791],{"class":49},[29,1832,1833,1835,1838],{"class":31,"line":176},[29,1834,1741],{"class":49},[29,1836,1837],{"class":45},"toList",[29,1839,1341],{"class":49},[29,1841,1842],{"class":31,"line":185},[29,1843,366],{"class":49},[15,1845,1846,1847,1850],{},"Local records are implicitly ",[26,1848,1849],{},"static",". They're ideal for naming intermediate pipeline\nvalues without polluting the top-level namespace.",[10,1852,1854],{"id":1853},"records-with-sealed-interfaces-and-pattern-matching","Records with sealed interfaces and pattern matching",[15,1856,1857],{},"Records compose naturally with sealed interfaces (Java 17) and switch pattern matching\n(Java 21) to model algebraic data types:",[19,1859,1861],{"className":21,"code":1860,"language":23,"meta":24,"style":24},"sealed interface Shape permits Circle, Rectangle, Triangle {}\nrecord Circle(double radius)          implements Shape {}\nrecord Rectangle(double w, double h)  implements Shape {}\nrecord Triangle(double base, double h) implements Shape {}\n\ndouble area(Shape s) {\n    return switch (s) {\n        case Circle(double r)             -> Math.PI * r * r;\n        case Rectangle(double w, double h) -> w * h;\n        case Triangle(double b, double h)  -> 0.5 * b * h;\n    };\n}\n",[26,1862,1863,1893,1912,1937,1962,1966,1976,1986,2015,2041,2073,2078],{"__ignoreMap":24},[29,1864,1865,1868,1871,1874,1877,1880,1882,1885,1887,1890],{"class":31,"line":32},[29,1866,1867],{"class":35},"sealed",[29,1869,1870],{"class":35}," interface",[29,1872,1873],{"class":45}," Shape",[29,1875,1876],{"class":35}," permits",[29,1878,1879],{"class":45}," Circle",[29,1881,104],{"class":49},[29,1883,1884],{"class":45},"Rectangle",[29,1886,104],{"class":49},[29,1888,1889],{"class":45},"Triangle",[29,1891,1892],{"class":49}," {}\n",[29,1894,1895,1897,1899,1901,1903,1906,1908,1910],{"class":31,"line":53},[29,1896,384],{"class":35},[29,1898,1879],{"class":45},[29,1900,94],{"class":49},[29,1902,871],{"class":35},[29,1904,1905],{"class":49}," radius)          ",[29,1907,993],{"class":35},[29,1909,1873],{"class":45},[29,1911,1892],{"class":49},[29,1913,1914,1916,1919,1921,1923,1926,1928,1931,1933,1935],{"class":31,"line":67},[29,1915,384],{"class":35},[29,1917,1918],{"class":45}," Rectangle",[29,1920,94],{"class":49},[29,1922,871],{"class":35},[29,1924,1925],{"class":49}," w, ",[29,1927,871],{"class":35},[29,1929,1930],{"class":49}," h)  ",[29,1932,993],{"class":35},[29,1934,1873],{"class":45},[29,1936,1892],{"class":49},[29,1938,1939,1941,1944,1946,1948,1951,1953,1956,1958,1960],{"class":31,"line":79},[29,1940,384],{"class":35},[29,1942,1943],{"class":45}," Triangle",[29,1945,94],{"class":49},[29,1947,871],{"class":35},[29,1949,1950],{"class":49}," base, ",[29,1952,871],{"class":35},[29,1954,1955],{"class":49}," h) ",[29,1957,993],{"class":35},[29,1959,1873],{"class":45},[29,1961,1892],{"class":49},[29,1963,1964],{"class":31,"line":86},[29,1965,83],{"emptyLinePlaceholder":82},[29,1967,1968,1970,1973],{"class":31,"line":138},[29,1969,871],{"class":35},[29,1971,1972],{"class":45}," area",[29,1974,1975],{"class":49},"(Shape s) {\n",[29,1977,1978,1980,1983],{"class":31,"line":156},[29,1979,1727],{"class":35},[29,1981,1982],{"class":35}," switch",[29,1984,1985],{"class":49}," (s) {\n",[29,1987,1988,1991,1993,1995,1997,2000,2002,2005,2007,2010,2012],{"class":31,"line":171},[29,1989,1990],{"class":35},"        case",[29,1992,1879],{"class":45},[29,1994,94],{"class":49},[29,1996,871],{"class":35},[29,1998,1999],{"class":49}," r)             ",[29,2001,1750],{"class":35},[29,2003,2004],{"class":49}," Math.PI ",[29,2006,1073],{"class":35},[29,2008,2009],{"class":49}," r ",[29,2011,1073],{"class":35},[29,2013,2014],{"class":49}," r;\n",[29,2016,2017,2019,2021,2023,2025,2027,2029,2031,2033,2036,2038],{"class":31,"line":176},[29,2018,1990],{"class":35},[29,2020,1918],{"class":45},[29,2022,94],{"class":49},[29,2024,871],{"class":35},[29,2026,1925],{"class":49},[29,2028,871],{"class":35},[29,2030,1955],{"class":49},[29,2032,1750],{"class":35},[29,2034,2035],{"class":49}," w ",[29,2037,1073],{"class":35},[29,2039,2040],{"class":49}," h;\n",[29,2042,2043,2045,2047,2049,2051,2054,2056,2058,2060,2063,2066,2069,2071],{"class":31,"line":185},[29,2044,1990],{"class":35},[29,2046,1943],{"class":45},[29,2048,94],{"class":49},[29,2050,871],{"class":35},[29,2052,2053],{"class":49}," b, ",[29,2055,871],{"class":35},[29,2057,1930],{"class":49},[29,2059,1750],{"class":35},[29,2061,2062],{"class":115}," 0.5",[29,2064,2065],{"class":35}," *",[29,2067,2068],{"class":49}," b ",[29,2070,1073],{"class":35},[29,2072,2040],{"class":49},[29,2074,2075],{"class":31,"line":205},[29,2076,2077],{"class":49},"    };\n",[29,2079,2080],{"class":31,"line":230},[29,2081,366],{"class":49},[15,2083,753,2084,2087,2088,2091,2092,2095,2096,2098],{},[26,2085,2086],{},"switch"," is ",[371,2089,2090],{},"exhaustive"," (compiler-checked because ",[26,2093,2094],{},"Shape"," is sealed) and the\ncomponents are destructured inline — no ",[26,2097,243],{}," casts, no accessor calls.",[15,2100,2101,2102,829],{},"This pattern (sealed interface + record variants + exhaustive switch) is the modern Java\nreplacement for the ",[371,2103,2104],{},"Visitor design pattern",[10,2106,2108],{"id":2107},"recap","Recap",[15,2110,2111,2114,2115,2117,2118,104,2120,2122,2123,2125,2126,2129,2130,2133,2134,2136,2137,2140,2141,2144,2145,1536,2147,2149,2150,1180,2153,2156],{},[371,2112,2113],{},"Records"," (Java 16) eliminate data-class boilerplate by generating ",[26,2116,448],{},",\n",[26,2119,451],{},[26,2121,455],{},", and component accessors from a one-line header. Fields are\n",[26,2124,422],{}," giving ",[371,2127,2128],{},"shallow immutability"," — copy mutable components in the compact\nconstructor for deep immutability. Validation in the compact constructor is enforced on\n",[371,2131,2132],{},"deserialization"," too, unlike regular classes. Records are ",[26,2135,924],{},", can't extend\nclasses, and can't add instance fields beyond components, but they freely implement\ninterfaces and carry static members and instance methods. They work with ",[371,2138,2139],{},"Jackson 2.12+","\nout of the box, make safe ",[371,2142,2143],{},"Map keys"," without manual ",[26,2146,448],{},[26,2148,451],{},", and compose\nwith ",[371,2151,2152],{},"sealed interfaces",[371,2154,2155],{},"switch pattern matching"," as a clean alternative to the\nVisitor pattern.",[2158,2159,2160],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":24,"searchDepth":53,"depth":53,"links":2162},[2163,2164,2165,2166,2167,2168,2169,2170,2171,2172,2173,2174],{"id":12,"depth":53,"text":13},{"id":404,"depth":53,"text":405},{"id":576,"depth":53,"text":577},{"id":749,"depth":53,"text":750},{"id":917,"depth":53,"text":918},{"id":1172,"depth":53,"text":1173},{"id":1294,"depth":53,"text":1295},{"id":1413,"depth":53,"text":1414},{"id":1523,"depth":53,"text":1524},{"id":1664,"depth":53,"text":1665},{"id":1853,"depth":53,"text":1854},{"id":2107,"depth":53,"text":2108},"Complete guide to Java records — record components, canonical and compact constructors, immutability caveats, records vs Lombok, Jackson integration, generic records, local records, serialisation, and use with sealed interfaces and pattern matching.","medium","md","Java",{},"\u002Fblog\u002Fjava-records","\u002Fjava\u002Fmodern-java\u002Frecords",{"title":5,"description":2175},"blog\u002Fjava-records","Modern Java","modern-java","2026-06-20","szTPmy1hZ75Veb5BlTsUwp4H3l6SUgZarG_TprdNMJY",1782244089459]