What are records in Java, and what does the compiler generate for you?
A record is a restricted form of class designed for carrying data. You declare the components in the header, and the compiler generates everything a data class needs: a private final field for each component, an all-args constructor that assigns them, accessors named after the component rather than getX, and equals, hashCode and toString based on the components.
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
public class Main {
public static void main(String[] args) {
Point p = new Point(3, 4);
System.out.println("point : " + p);
System.out.println("x : " + p.x());
System.out.println("equals : " + p.equals(new Point(3, 4)));
System.out.println("hashCode : " + (p.hashCode() == new Point(3, 4).hashCode()));
// Works as a map key and inside a set.
var points = new HashSet<Point>(List.of(p, new Point(3, 4)));
System.out.println("in a set : " + points.size());
}
}
// What a record compiles down to: a final class with a private final field
// per component, an all-args constructor, and accessors named after the
// component rather than getX.
final class Point {
private final int x;
private final int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
int x() {
return x;
}
int y() {
return y;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Point)) {
return false;
}
Point other = (Point) o;
return x == other.x && y == other.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
@Override
public String toString() {
return "Point[x=" + x + ", y=" + y + "]";
}
}The sample above is that generated code written out by hand, so you can run it and see exactly what the compiler produces. The toString prints both components in a readable form, the accessors are named x and y rather than getX, two points with the same values are equal and share a hash code, and the set collapses them into a single entry.
Three things to notice about the design. The accessors are x() rather than And the components are implicitly final as well, because a mutable component would break the hash code the compiler generated.getX(), which was a deliberate break with the JavaBean convention and gives records their own recognisable style. The class is implicitly final, because a record that could be subclassed could break the equals contract the compiler generated.
Records also give you a compact form of inheritance that the class syntax cannot: a record may implement interfaces, so it can be a Comparable or a Serializable, but it cannot extend another class.