Records and Sealed Classes
Modern Java is moving away from the “everything is a complex object” philosophy toward Data-Oriented Programming. Two of the most important tools for this are Records and Sealed Classes.
1. Records (Data Carriers)
Before Java 14, creating a simple data carrier (POJO) required dozens of lines of boilerplate (Getters, equals, hashCode, toString).
- A Record is an immutable data carrier that generates all of this for you in one line.
public record User(String name, String email, int age) {}
Key Features of Records:
- Immutable: All fields are
finalby default. - Concise: No more boilerplate.
- Canonical Constructor: Automatically handles assignment.
- Validation: You can add validation logic in a “Compact Constructor.”
public record User(String name, String email) {
public User {
if (name.isBlank()) throw new IllegalArgumentException("Name cannot be blank");
}
}
2. Sealed Classes (Restricted Hierarchies)
Traditionally, a class could be extended by anything unless it was final. Sealed Classes give you a middle ground: you define exactly which classes are allowed to extend your class.
public sealed class Shape permits Circle, Square, Triangle {}
public final class Circle extends Shape { ... }
public final class Square extends Shape { ... }
public final class Triangle extends Shape { ... }
Why use Sealed Classes?
- Security: Prevents unauthorized subclasses.
- Exhaustiveness: When using
switchexpressions (covered in the next chapter), the compiler knows exactly all possible types, so you don’t need adefaultcase.
3. Interactive: Boilerplate Shredder
See how much code you save using Records.
Legacy Class (Java 8)
public class User {
private final String name;
public User(String n) {this.name=n;}
public String getName() {return name;}
@Override public boolean equals(Object o)...
@Override public int hashCode()...
@Override public String toString()...
}
➡️
Modern Record (Java 17+)
record User(String name) {}
4. Best Practices
- DTOs: Use Records for Data Transfer Objects (fetching data from DB or API).
- Domain Logic: Use Sealed Classes for State Machines or Command patterns.
- Immutability: Remember that while a Record is final, its fields (like a List) may still be mutable elements themselves. Always use
List.of()orCollections.unmodifiableList().