Java Sealed Classes
A sealed class or interface limits which classes may extend it, naming them in a permits list. The compiler knows the whole family, so a switch needs no default branch.
A switch with no default covers the family
Shape permits Circle and Square only. The switch covers both and prints one area per shape.
public class Main {
static String describe(Shape shape) {
return switch (shape) {
case Circle c -> "Circle area " + String.format("%.2f", Math.PI * c.radius() * c.radius());
case Square s -> "Square area " + String.format("%.2f", s.side() * s.side());
};
}
public static void main(String[] args) {
System.out.println(describe(new Circle(2)));
System.out.println(describe(new Square(3)));
}
}
sealed interface Shape permits Circle, Square {
}
record Circle(double radius) implements Shape {
}
record Square(double side) implements Shape {
}Deleting one branch breaks the switch
The same switch loses its Square branch. The compiler refuses it, and the refusal prints below.
public class Main {
static String describe(Shape shape) {
return switch (shape) {
case Circle c -> "Circle area " + String.format("%.2f", Math.PI * c.radius() * c.radius());
};
}
public static void main(String[] args) {
System.out.println(describe(new Circle(1)));
}
}
sealed interface Shape permits Circle, Square {
}
record Circle(double radius) implements Shape {
}
record Square(double side) implements Shape {
}A class outside the list is refused
Triangle tries to join the family from outside the permits list. The compiler refuses it, and the refusal prints below.
public class Main {
public static void main(String[] args) {
System.out.println("family of two");
}
}
sealed interface Shape permits Circle, Square {
}
record Circle(double radius) implements Shape {
}
record Square(double side) implements Shape {
}
class Triangle implements Shape {
}Example
import java.util.List;
public class Main {
static String describe(Shape shape) {
return switch (shape) {
case Circle c -> "Circle area " + String.format("%.2f", Math.PI * c.radius() * c.radius());
case Square s -> "Square area " + String.format("%.2f", s.side() * s.side());
};
}
public static void main(String[] args) {
List<Shape> shapes = List.of(new Circle(1), new Square(4));
for (Shape shape : shapes) {
System.out.println(describe(shape));
}
}
}
sealed interface Shape permits Circle, Square {
}
record Circle(double radius) implements Shape {
}
record Square(double side) implements Shape {
}Key points
- The permits clause lists every class allowed in the family.
- Every permitted subclass is final, sealed or non-sealed.
- A record is final, so records pair well with sealed types.
- A switch over a sealed type must cover every permitted class.
- A class outside the permits list cannot join the family.
In this example, a list holds one Circle and one Square, and the switch prints each area with no default branch.
Note
Sealing closes a family on purpose, and a new member needs a permits change.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
Why can the switch over Shape leave out the default branch?