Working with Records (Java 14+)

1. Creating Record Classes

ElementAuto-generated
Canonical constructorFrom components
Accessorsname(), age() (no get prefix)
equals/hashCodeComponent-based
toStringComponent-listing
ModifiersImplicitly final, extends java.lang.Record

Example: Basic record

public record Point(double x, double y) { }
Point p = new Point(1.0, 2.0);
double x = p.x();

2. Understanding Implicit Components

ComponentGenerated
Fieldprivate final per component
Accessor methodSame name as component
Canonical ctorParam order = declaration order
ReflectiongetRecordComponents()

3. Adding Custom Methods

AllowedNotes
Static methods/fieldsYes
Instance methodsYes (no extra state)
Override accessorAllowed
Add instance fieldsForbidden

4. Using Compact Constructors

FormBehavior
CompactValidates/normalizes; no field assignment
CanonicalFull param list; manual assignment
CustomMust delegate to canonical

Example: Compact validator

public record Range(int lo, int hi) {
    public Range {
        if (lo > hi) throw new IllegalArgumentException("lo > hi");
    }
}

5. Implementing Interfaces with Records

AspectDetail
ImplementsAllowed (multiple interfaces)
ExtendsCannot extend a class
Sealed interfaceCommon pattern with records

6. Using Records with Generics

Example: Generic Pair

public record Pair<A, B>(A first, B second) {
    public <C> Pair<A, C> withSecond(C c) { return new Pair<>(first, c); }
}
FeatureSupported
Type parametersYes (on declaration)
BoundedYes
WildcardsYes in components

7. Understanding Record Restrictions

RestrictionReason
Implicitly finalNo subclassing
No extendsAlready extends Record
No instance init blocksUse compact ctor
No native methodsDisallowed
No additional instance fieldsComponents only

8. Serializing Records

AspectBehavior
Default formComponents only; canonical ctor on read
writeObject/readObjectIgnored
serialVersionUIDOptional but recommended
SafetyValidation runs during deserialization

9. Using Records in Pattern Matching

Example: Record patterns (Java 21+)

sealed interface Shape permits Circle, Rect { }
record Circle(double r) implements Shape { }
record Rect(double w, double h) implements Shape { }

double area = switch (s) {
    case Circle(double r) -> Math.PI * r * r;
    case Rect(double w, double h) -> w * h;
};
PatternUse
Type patterncase Circle c
Record patterncase Circle(double r)
Nestedcase Rect(Length(int v), _)

10. Understanding Record Performance

AspectDetail
equals/hashCodeGenerated via invokedynamic (ObjectMethods)
InliningJIT inlines accessors
MemorySame as plain final class
FutureValue classes (Project Valhalla)

11. Using Local Records (Java 16+)

FeatureDetail
WhereInside a method body
Implicitlystatic — cannot capture enclosing instance
UseLocal data carriers in stream pipelines

Example: Local record in stream

List<String> topNames(List<User> users) {
    record Score(String name, int s) { }
    return users.stream()
        .map(u -> new Score(u.name(), u.points()))
        .sorted(Comparator.comparingInt(Score::s).reversed())
        .map(Score::name).limit(10).toList();
}