A sealed type (Java 17) lists exactly which classes may extend it. The compiler then knows the full set of subtypes — which makes switch over them exhaustive.
Why seal a hierarchy?
Subtype modifiers
| Permitted subtype is… | Meaning |
|---|---|
final | No further subclasses (records and enums are implicitly final) |
sealed … permits … | Continues the restriction one level down |
non-sealed | Re-opens that branch — anyone may extend it |
Sealed interface + records + pattern switch
public class Main {
sealed interface Shape permits Circle, Square, Rect {}
record Circle(double r) implements Shape {}
record Square(double side) implements Shape {}
record Rect(double w, double h) implements Shape {}
static double area(Shape s) {
return switch (s) { // exhaustive: no default
case Circle c -> Math.PI * c.r() * c.r();
case Square sq -> sq.side() * sq.side();
case Rect(double w, double h) -> w * h; // record pattern (Java 21)
};
}
public static void main(String[] args) {
Shape[] shapes = { new Circle(1), new Square(2), new Rect(2, 3) };
for (Shape s : shapes) System.out.printf("%s -> %.2f%n", s, area(s));
}
}Circle[r=1.0] -> 3.14
Square[side=2.0] -> 4.00
Rect[w=2.0, h=3.0] -> 6.00Sealed classes with final / non-sealed
public class Main {
static abstract sealed class Vehicle permits Car, Truck {}
static final class Car extends Vehicle {}
static non-sealed class Truck extends Vehicle {} // open again
static class Pickup extends Truck {} // allowed: Truck is non-sealed
static String describe(Vehicle v) {
return switch (v) {
case Car c -> "car";
case Truck t -> "truck (" + t.getClass().getSimpleName() + ")";
};
}
public static void main(String[] args) {
System.out.println(describe(new Car()));
System.out.println(describe(new Pickup()));
}
}car
truck (Pickup)Trying to extend a sealed type
public class Main {
sealed interface Shape permits Circle {}
record Circle(double r) implements Shape {}
record Triangle(double b, double h) implements Shape {} // not in permits
public static void main(String[] args) {}
}Main.java:4: error: class is not allowed to extend sealed class: Shape (as it is not listed in its 'permits' clause)
record Triangle(double b, double h) implements Shape {} // not in permits
^Permitted subclasses must be in the same module (or the same package in the unnamed module).
If all subtypes are in the same source file, the
permitsclause can be omitted — the compiler infers it.Every permitted subtype must directly extend/implement the sealed type.
Sealed class vs final class vs enum?
What does sealing buy you in a switch?