Reading — step 1 of 6
Learn
Records (Java 14+) are concise, immutable data carriers. They auto-generate the constructor, accessors, equals, hashCode, and toString — replacing 30+ lines of boilerplate with one declaration.
The basic form
record Point(int x, int y) {}
Point p = new Point(3, 4);
p.x(); // 3 — auto-generated accessor (note the parens)
p.y(); // 4
p.toString(); // Point[x=3, y=4]
p.equals(new Point(3, 4)); // true (structural equality)
The header Point(int x, int y) is both the field declaration AND the constructor signature.
What records auto-generate
For record Point(int x, int y) {}, Java synthesizes:
final class Point {
private final int x;
private final int y;
public Point(int x, int y) { this.x = x; this.y = y; }
public int x() { return x; }
public int y() { return y; }
public boolean equals(Object o) { /* compares all components */ }
public int hashCode() { /* hashes all components */ }
public String toString() { /* Point[x=3, y=4] */ }
}
All fields are private final — records are immutable. There's no setter.
Adding methods
Records can have methods:
record Point(int x, int y) {
public double distance(Point other) {
int dx = x - other.x;
int dy = y - other.y;
return Math.sqrt(dx * dx + dy * dy);
}
public Point translate(int dx, int dy) {
return new Point(x + dx, y + dy); // returns a NEW Point
}
}
Validation in compact constructor
Records support a compact constructor for validation:
record Email(String address) {
public Email {
if (address == null || !address.contains("@")) {
throw new IllegalArgumentException("invalid email");
}
}
}
The parameters are implicit. Field assignment happens AFTER the body. Use this for sanitization or invariant checks.
When to use records
- DTOs (data transfer objects)
- Multiple return values:
record Result<T>(T value, String error) {} - API response shapes
- Domain models that are pure data with derived methods
When NOT to use records
- Mutable state — records are immutable by design
- Inheritance hierarchies — records are implicitly final
- Frameworks that need a no-args constructor (older Hibernate, etc.)
vs. classes
// Old way (35+ lines):
public class Point {
private final int x;
private final int y;
public Point(int x, int y) { this.x = x; this.y = y; }
public int getX() { return x; }
public int getY() { return y; }
@Override public boolean equals(Object o) { ... }
@Override public int hashCode() { ... }
@Override public String toString() { ... }
}
// Record (one line):
record Point(int x, int y) {}
For immutable data, records are the modern default.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…