Java Records β Modern Data Classes (Java 14+)
A record is a compact syntax for declaring an immutable data class. One line replaces a constructor, getters, equals, hashCode and toString. Records shipped in Java 14 as a preview and went final in Java 16.
One line, full behaviour
public record Point(double x, double y) {}
Point p = new Point(3, 4);
p.x(); // 3.0 β no get prefix
p.y(); // 4.0
p.equals(new Point(3, 4)); // true
p.toString(); // Point[x=3.0, y=4.0]
p.hashCode(); // consistent with equals
What the compiler generates
For record Point(double x, double y) {}:
- Two private final fields:
x,y. - A canonical constructor:
Point(double x, double y). - Two accessors:
x(),y()β no "get" prefix. equals,hashCode,toStringbased on all components.- The class is implicitly
finaland extendsjava.lang.Record.
Compact constructor β for validation
public record Circle(double radius) {
public Circle { // no parameter list, no braces on fields
if (radius < 0) throw new IllegalArgumentException("radius < 0");
// fields are assigned automatically after this block
}
}
Adding methods
public record Circle(double radius) {
public double area() { return Math.PI * radius * radius; }
public boolean contains(double x, double y) {
return x * x + y * y <= radius * radius;
}
}
Records and interfaces
public sealed interface Shape permits Circle, Square {}
public record Circle(double radius) implements Shape {}
public record Square(double side) implements Shape {}
Limitations
- Implicitly
finalβ cannot be extended. - Cannot
extendsanother class (always extendsRecord). - Cannot declare instance fields beyond the components β all state must be in the header.
- No no-arg constructor unless all components have defaults (workaround: add a static factory).
Record vs class vs Lombok @Data
| Record | Plain class | Lombok @Data | |
|---|---|---|---|
| Immutable | Always | You choose | No (setters generated) |
| Extra code | None | Lots | Small annotation |
| External dep | No | No | Lombok |
| Inheritance | No (final) | Yes | Yes |
| Best for | Data carriers | Behaviour-rich | Mutable beans (JPA) |
Common mistakes
- Using a record when behaviour will grow β records are meant for data. If the class needs state evolution, use a normal class.
- Returning a mutable collection in an accessor β wrap with
List.copyOfor, better, pass an immutable list to the constructor. - Fighting the immutability β if you need setters, you don't want a record.
Related
Pillar: OOP. Tool: JSON to POJO β generates records from JSON. Siblings: classes, sealed classes.