Records
Picture an application form with the boxes "name", "date" and "signature". Nobody edits a form once it is filled in and handed over: if something must change, you fill in a new one. On the other hand, you can compare two forms box by box: if every box says the same thing, the applications are the same.
Remember the Point class from the theme on classes. To create a point with its coordinates at once, print it and compare it, you would have to add a constructor, getters, toString and a comparing method. That is dozens of lines, although the point itself is only two numbers. For classes that only hold data, Java 16 brought records:
public class Main {
public static void main(String[] args) {
Point a = new Point(3, 4);
Point b = new Point(3, 4);
System.out.println(a); // Point[x=3, y=4]
System.out.println(a.x()); // 3
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
}
}
record Point(int x, int y) {}Let's go through the program step by step:
record Point(int x, int y) {}is the whole declaration. After the name, the parentheses list the components, the boxes of the form, and the body in braces is empty.new Point(3, 4)calls a constructor, even though nobody wrote one.println(a)printsPoint[x=3, y=4]: the record's name and every box.a.x()reads the componentx. It is a method, and it is named like the component, withoutget.a == bgivesfalse: these are two different objects, two separate forms.a.equals(b)givestrue: records are compared by their components, and both hold3and4.
What the compiler writes
A record is a class in which you declare only the data, and the compiler writes the rest for you. The line record Point(int x, int y) {} gives:
- the fields
private final int xandprivate final int y; - a constructor with the parameters
xandyin the same order; - the accessor methods
x()andy(); toStringin the formPoint[x=3, y=4];equals, which compares the components one by one, andhashCode, which gives equal records the same number.
Interviewers like to ask this: how does a record differ from an ordinary class? The answer: a record holds immutable data, and the compiler writes its constructor, accessor methods, equals, hashCode and toString.
A record can implement an interface, like an ordinary class. But it cannot extend another class, and no class can extend it either.
A record cannot be changed
The fields of a record are final: after the constructor nobody changes them, the form is handed over. To "change" a point, you create a new one:
Point p = new Point(3, 4);
p = new Point(5, p.y());
System.out.println(p); // Point[x=5, y=4]The second line builds a new record: its x is 5, and its y is carried over from the old one through p.y(). The variable p now refers to the new object, and the old point stays as it was.
Here the form picture is not exact. If a record component is an array, the box holds not the row of lockers but a ticket with its address. The ticket cannot be replaced, but the contents of the lockers can be changed. Only the boxes themselves are immutable.
The compact constructor
Sometimes the data must be checked or fixed while the form is not handed over yet. For that, a record has a compact constructor: no parameter list, only the record's name and a body:
public class Main {
public static void main(String[] args) {
System.out.println(new Range(5, 1)); // Range[from=1, to=5]
}
}
record Range(int from, int to) {
Range {
if (from > to) {
int t = from;
from = to;
to = t;
}
}
}Let's see how it works:
Range { … }is the compact constructor: its parametersfromandtoare the same as the record's.- If
fromis greater thanto, the body swaps the parameters through the variablet. - After the body, the compiler itself adds the assignments
this.from = from;andthis.to = to;, so the fields get the values already fixed. new Range(5, 1)prints asRange[from=1, to=5].
Common mistake
Someone tried to change a coordinate of a record:
Point p = new Point(3, 4);
p.x = 5; // ✗error: x has private access in PointThe program does not compile. The fields of a record are private, so from outside you read them only through the method p.x(). Inside the record you cannot assign to a field either: it is final, and the compiler answers cannot assign a value to final variable x. To get a point with a different x, create a new record, as in the section above.
Key points
record Point(int x, int y) {}declares the data, and the compiler writes the constructor, the methodsx()andy(),toString,equalsandhashCode.- A record's
equalscompares the components: two records with the same data are equal, even though==givesfalsefor them. - The fields of a record are
private final: you do not change a record, you create a new one. - A compact constructor checks or fixes the values before they reach the fields.