Sealed Classes and Interfaces in Java
A payment in a shop is UPI, card or cash, and nothing else. With an ordinary interface, any class anywhere could implement Payment, so code that handles "every kind of payment" can never be sure it really has. A sealed class or interface (standard since Java 17) lists its permitted subtypes. The compiler then knows the complete set, rejects anything else, and can check that a switch handles every case, with no default needed.
A Closed Set of Shapes
public class Shapes {
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) {
case Circle c -> Math.PI * c.r() * c.r();
case Square q -> q.side() * q.side();
case Rect r -> r.w() * r.h();
};
}
public static void main(String[] args) {
Shape[] shapes = { new Circle(1), new Square(4), 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=4.0] -> 16.00
Rect[w=2.0, h=3.0] -> 6.00How it works
sealed interface Shape permits Circle, Square, Rectsays exactly three types may implementShape.- Each is a record, and records are implicitly
final, which satisfies the sealing rules below. - The
switchuses type patterns:case Circle cmatches aCircleand names itc(a later lesson covers patterns fully). - Because the compiler knows
Shapehas exactly three subtypes, and all three are handled, the switch is exhaustive: nodefaultneeded.
The Rules
Every permitted subclass must say how it continues the hierarchy, with exactly one of three modifiers:
| Modifier on the subclass | Meaning |
|---|---|
final | No further subclasses (records and enums are implicitly final) |
sealed | It has its own permits list |
non-sealed | Open again: anyone may extend it |
sealed abstract class Payment permits Upi, Card, Cash { }
final class Upi extends Payment { } // closed
sealed class Card extends Payment permits Credit, Debit { }
final class Credit extends Card { }
final class Debit extends Card { }
non-sealed class Cash extends Payment { } // open to extensionFurther rules:
- Permitted subclasses must be in the same module (or, without modules, the same package) as the sealed type.
- If all subclasses are in the same file, the
permitsclause can be left out; the compiler infers it.
The Errors
A class that isn't listed:
sealed interface Shape permits Circle, Square { }
record Circle(double r) implements Shape { }
record Square(double side) implements Shape { }
record Triangle(double b, double h) implements Shape { }S1.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 { }
^
1 errorA permitted subclass with no modifier:
sealed abstract class Payment permits Upi, Card { }
final class Upi extends Payment { }
class Card extends Payment { }S2.java:3: error: sealed, non-sealed or final modifiers expected
class Card extends Payment { }
^
1 errorAnd if a switch forgets a permitted type, it no longer compiles: error: the switch expression does not cover all possible input values. Add a new shape to the permits list and every switch that doesn't handle it breaks at compile time, pointing you to exactly the code that needs updating.
Common Mistakes
Forgetting final, sealed or non-sealed on a permitted class. Records and enums get final automatically; ordinary classes don't.
Adding a default to an exhaustive switch. It works, but it silences the compiler error you'd want when a new subtype appears.
Permitting classes in another package without modules. Keep the hierarchy together.
Interview Corner
What are sealed classes, and why were they added?
Sealed classes and interfaces, standard since Java 17, restrict which classes can extend or implement them through a permits clause. Each permitted subclass must be final, sealed or non-sealed. They let a library author model a closed set of alternatives, keep the hierarchy under control, and let the compiler check that switch statements and expressions over the sealed type are exhaustive.
What is the difference between final, sealed and non-sealed?
A final class can't be extended at all. A sealed class can be extended only by the classes listed in its permits clause. A non-sealed class is a permitted subclass that re-opens the hierarchy, so any class may extend it.
Practice
- Easy: Model
sealed interface Payment permits Upi, Card, Cashwith three records, and write a method that returns a description for each using aswitch. - Harder: Add a
Walletrecord toPayment's permits list without updating the switch. Read the compile error, then fix it.
Key Takeaways
Was this tutorial helpful?