An ordinary interface can be implemented by anyone, anywhere. A sealed interface declares exactly which types are allowed to implement it, which unlocks exhaustive pattern matching.
Declare the permitted set
Only the listed types may extend or implement a sealed type.
public sealed interface Shape permits Circle, Rectangle, Triangle {}
public record Circle(double radius) implements Shape {}
public record Rectangle(double w, double h) implements Shape {}
public record Triangle(double base, double height) implements Shape {}Exhaustive switches
Because the compiler knows every case, a switch needs no default — and adding
a new subtype turns forgotten branches into compile errors.
double area = switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.w() * r.h();
case Triangle t -> 0.5 * t.base() * t.height();
};Takeaways
- Sealed types model closed hierarchies like algebraic data types.
- Combine with records and pattern matching for concise, safe modeling.
- The compiler enforces exhaustiveness, catching missed cases at build time.
Permitted subclasses must be final, sealed, or explicitly non-sealed, and
must live in the same module or package.