Working with Records (Java 14+)
1. Creating Record Classes
| Element | Auto-generated |
|---|---|
| Canonical constructor | From components |
| Accessors | name(), age() (no get prefix) |
equals/hashCode | Component-based |
toString | Component-listing |
| Modifiers | Implicitly 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
| Component | Generated |
|---|---|
| Field | private final per component |
| Accessor method | Same name as component |
| Canonical ctor | Param order = declaration order |
| Reflection | getRecordComponents() |
3. Adding Custom Methods
| Allowed | Notes |
|---|---|
| Static methods/fields | Yes |
| Instance methods | Yes (no extra state) |
| Override accessor | Allowed |
| Add instance fields | Forbidden |
4. Using Compact Constructors
| Form | Behavior |
|---|---|
| Compact | Validates/normalizes; no field assignment |
| Canonical | Full param list; manual assignment |
| Custom | Must 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
| Aspect | Detail |
|---|---|
| Implements | Allowed (multiple interfaces) |
| Extends | Cannot extend a class |
| Sealed interface | Common 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); }
}
| Feature | Supported |
|---|---|
| Type parameters | Yes (on declaration) |
| Bounded | Yes |
| Wildcards | Yes in components |
7. Understanding Record Restrictions
| Restriction | Reason |
|---|---|
| Implicitly final | No subclassing |
| No extends | Already extends Record |
| No instance init blocks | Use compact ctor |
| No native methods | Disallowed |
| No additional instance fields | Components only |
8. Serializing Records
| Aspect | Behavior |
|---|---|
| Default form | Components only; canonical ctor on read |
writeObject/readObject | Ignored |
serialVersionUID | Optional but recommended |
| Safety | Validation 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;
};
| Pattern | Use |
|---|---|
| Type pattern | case Circle c |
| Record pattern | case Circle(double r) |
| Nested | case Rect(Length(int v), _) |
10. Understanding Record Performance
| Aspect | Detail |
|---|---|
| equals/hashCode | Generated via invokedynamic (ObjectMethods) |
| Inlining | JIT inlines accessors |
| Memory | Same as plain final class |
| Future | Value classes (Project Valhalla) |
11. Using Local Records (Java 16+)
| Feature | Detail |
|---|---|
| Where | Inside a method body |
| Implicitly | static — cannot capture enclosing instance |
| Use | Local data carriers in stream pipelines |