Objects, Classes & OOP Design
Records
A record declares immutable data in one line: the compiler generates the constructor, accessors,
equals, hashCode, and toString from the components. Use records for every value-like class — DTOs, keys, results, coordinates.- Components become
private finalfields + accessors named after them (point.x()) - Generated
equals/hashCode/toStringare componentwise and always in sync - Compact constructors validate or normalize without repeating parameters
- Records are implicitly final and cannot extend a class (they may implement interfaces)
- "Shallowly immutable": a
Listcomponent is still whatever list you passed in — copy it - Records + sealed interfaces + pattern matching = algebraic data types
public record Range(int low, int high) {
public Range { // compact constructor: validation
if (low > high)
throw new IllegalArgumentException(low + " > " + high);
}
public int length() { return high - low; } // extra methods are fine
}The compact constructor body runs before the fields are assigned — assign to the parameters to normalize (this.low = low happens implicitly afterward). Static factories, static fields, and instance methods are all allowed; instance fields beyond the components are not — a record is its components.
sealed interface Shape permits Circle, Rect {}
record Circle(double radius) implements Shape {}
record Rect(double w, double h) implements Shape {}
double area(Shape s) {
return switch (s) {
case Circle(double r) -> Math.PI * r * r; // record deconstruction
case Rect(double w, double h) -> w * h;
}; // exhaustive — no default needed
}This trio — sealed types, records, and pattern matching — brings algebraic data types to Java: closed sets of variants the compiler checks exhaustively. It replaces visitor-pattern boilerplate for tree-shaped data.