Skip to main content

12.4 Records and Sealed Classes

Java 14 (preview) → 16 (stable) introduced Records, and Java 17 introduced Sealed Classes. Both are core features of modern Java that make code more concise and safer.

1. Records​

When creating a simple class to hold data, you used to write all of the following manually:

  • private final field declarations
  • Constructor
  • getter methods
  • toString(), equals(), hashCode() overrides

A record generates all of this from a single line!

// Old approach: data class (very verbose)
public class PersonOld {
private final String name;
private final int age;

public PersonOld(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }

@Override
public String toString() {
return "PersonOld[name=" + name + ", age=" + age + "]";
}
// equals(), hashCode() omitted...
}

// Record approach: just 1 line!
public record Person(String name, int age) {}
public class RecordExample {
public static void main(String[] args) {
Person p = new Person("Alice", 25);

// Accessors are the field name (no "get" prefix)
System.out.println(p.name()); // Alice
System.out.println(p.age()); // 25

// toString, equals, hashCode provided automatically
System.out.println(p); // Person[name=Alice, age=25]

Person p2 = new Person("Alice", 25);
System.out.println(p.equals(p2)); // true (value comparison)
}
}

Records are immutable data objects — there are no setters, and fields cannot be changed after construction. They are ideal for API response data, DTOs (Data Transfer Objects), and value objects.

Compact Constructors​

Records support compact constructors for adding validation:

public record Range(int min, int max) {
// Compact constructor — no parameter list, fields assigned automatically
Range {
if (min > max) {
throw new IllegalArgumentException("min must be <= max");
}
}
}

Range valid = new Range(1, 10); // OK
Range invalid = new Range(10, 1); // throws IllegalArgumentException

2. Sealed Classes​

A sealed class explicitly restricts which classes may extend it. Use the sealed keyword with a permits clause.

// Shape can only be extended by Circle, Rectangle, and Triangle!
public sealed class Shape
permits Circle, Rectangle, Triangle {

public abstract double area();
}

// Each subclass must be declared final, sealed, or non-sealed
public final class Circle extends Shape {
private final double radius;
public Circle(double radius) { this.radius = radius; }

@Override
public double area() { return Math.PI * radius * radius; }
}

public final class Rectangle extends Shape {
private final double width, height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}

@Override
public double area() { return width * height; }
}

Sealed classes are especially powerful when combined with Java 21 pattern matching:

// Java 21 - switch pattern matching
static String describe(Shape shape) {
return switch (shape) {
case Circle c -> "Circle, area: " + c.area();
case Rectangle r -> "Rectangle, area: " + r.area();
default -> "Unknown shape";
};
}

Records and sealed classes are key modern Java features that make the language more expressive, safe, and easy to maintain.