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, toString based on all components.
  • The class is implicitly final and extends java.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 extends another class (always extends Record).
  • 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

RecordPlain classLombok @Data
ImmutableAlwaysYou chooseNo (setters generated)
Extra codeNoneLotsSmall annotation
External depNoNoLombok
InheritanceNo (final)YesYes
Best forData carriersBehaviour-richMutable 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.copyOf or, 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.