Sealed Classes and Interfaces in Java (Java 17+)
A sealed class or interface restricts which other classes can extend or implement it. You list them with permits. Subclasses must themselves be final, sealed, or non-sealed. Sealed hierarchies are the foundation for exhaustive switches (Java 21+) β the compiler can prove you've handled every case.
Syntax
public sealed interface Shape permits Circle, Square, Triangle {}
public final record Circle(double r) implements Shape {}
public final record Square(double s) implements Shape {}
public final record Triangle(double b, double h) implements Shape {}
What "sealed" really buys you
String describe(Shape s) {
return switch (s) {
case Circle c -> "circle " + c.r();
case Square sq -> "square " + sq.s();
case Triangle t -> "triangle " + t.b() + "x" + t.h();
// no default needed β the compiler knows the list is complete
};
}
Add a new permitted type and the switch becomes a compile error until you handle it. That's the point.
Subclass modifiers
finalβ closes the hierarchy at this subclass. Records are implicitlyfinal.sealedβ further restricts with its ownpermitsclause.non-sealedβ opens this branch back up; anyone can extend it.
public sealed class Vehicle permits Car, Truck, SpecialVehicle {}
public final class Car extends Vehicle {}
public non-sealed class SpecialVehicle extends Vehicle {} // open β anyone can subclass
public sealed class SportsCar extends Car permits Ferrari {} // β Car is final
Same package/module rule
All permitted subclasses must be in the same module (or, if not modularised, the same package). This is what makes sealing reliable β you can see the full hierarchy from one place.
When to seal
- Algebraic data types β an expression tree, a result that is either
OkorErr. - State machines β a finite set of states you want to handle exhaustively.
- Public APIs with a known closed set β a library author wants to prevent users from adding new variants.
Common mistakes
- Forgetting
permitswhen the subclasses aren't in the same file β compile error. - Mixing
defaultwith sealed-typeswitchβ often unnecessary; the compiler proves exhaustiveness without it. Removing thedefaultre-enables the compile error when a new variant is added. - Sealing an open ecosystem β if external code must add variants,
sealedis wrong. Use a normal interface.
Related
Pillar: OOP. Siblings: records, interfaces, switch pattern matching.