Sealed Classes in Java
Learn how sealed classes control inheritance and define restricted class hierarchies.
1. What are Sealed Classes?
A sealed class is a Java class or interface that restricts which classes or interfaces are allowed to extend or implement it.
Sealed classes are useful when an application needs a controlled and known inheritance hierarchy.
2. Java Version
| Java Version | Sealed Classes |
|---|---|
| Java 15 | First preview |
| Java 16 | Second preview |
| Java 17 | Standard feature |
3. Basic Sealed Class Syntax
The sealed keyword is followed by the permits
clause that lists the allowed subclasses.
public sealed class Vehicle
permits Car, Bike {
}
public final class Car
extends Vehicle {
}
public final class Bike
extends Vehicle {
}
Here, only Car and Bike are permitted to
directly extend Vehicle.
4. permits Keyword
The permits clause defines the direct subclasses that
are allowed to inherit from the sealed type.
sealed interface Payment
permits CardPayment, CashPayment {
}
final class CardPayment
implements Payment {
}
final class CashPayment
implements Payment {
}
5. final Subclasses
A permitted subclass can be declared final when it should
not have any further subclasses.
sealed class Animal
permits Dog, Cat {
}
final class Dog
extends Animal {
}
final class Cat
extends Animal {
}
6. non-sealed Classes
A permitted subclass can use the non-sealed modifier
to reopen the inheritance hierarchy.
sealed class Vehicle
permits Car, Truck {
}
non-sealed class Car
extends Vehicle {
}
class SportsCar
extends Car {
}
Because Car is non-sealed, other classes
such as SportsCar can extend it.
7. A Sealed Subclass
A permitted subclass can itself remain sealed and restrict its own subclasses.
sealed class Vehicle
permits Car {
}
sealed class Car
extends Vehicle
permits ElectricCar {
}
final class ElectricCar
extends Car {
}
8. Three Possible Subclass Modifiers
Every direct subclass of a sealed class must continue the inheritance policy using one of three modifiers:
finalsealednon-sealed
| Modifier | Meaning |
|---|---|
final |
No further inheritance is allowed. |
sealed |
Inheritance continues but remains restricted. |
non-sealed |
Inheritance is reopened. |
9. Sealed Interfaces
Interfaces can also be sealed.
public sealed interface Shape
permits Circle, Rectangle {
}
final class Circle
implements Shape {
}
final class Rectangle
implements Shape {
}
10. Sealed Interfaces with Multiple Implementations
sealed interface Payment
permits CardPayment,
UpiPayment,
CashPayment {
}
final class CardPayment
implements Payment {
}
final class UpiPayment
implements Payment {
}
final class CashPayment
implements Payment {
}
11. Location of Permitted Classes
In the common named-package case, permitted direct subclasses must belong to the same package as the sealed type.
The Java language also provides rules for unnamed modules and named modules, so the exact source-location rules depend on the module/package structure of the application.
12. Checking Sealed Types at Runtime
Java reflection can determine whether a class is sealed and inspect its permitted subclasses.
sealed class Vehicle
permits Car, Bike {
}
final class Car
extends Vehicle {
}
final class Bike
extends Vehicle {
}
public class Main {
public static void main(String[] args) {
System.out.println(
Vehicle.class.isSealed()
);
Class<?>[] permitted =
Vehicle.class.getPermittedSubclasses();
for (Class<?> type : permitted) {
System.out.println(
type.getName()
);
}
}
}
13. Sealed Classes and Pattern Matching
Sealed hierarchies work particularly well with modern pattern matching.
Since the compiler knows the permitted subclasses, switch expressions
can often be exhaustive without a default branch.
sealed interface Shape
permits Circle, Rectangle {
}
final class Circle
implements Shape {
}
final class Rectangle
implements Shape {
}
static String describe(Shape shape) {
return switch (shape) {
case Circle circle ->
"Circle";
case Rectangle rectangle ->
"Rectangle";
};
}
14. Sealed Classes for Domain Modeling
Sealed classes are useful when a domain has a fixed set of possible states or types.
Examples include:
- Payment types
- Notification types
- Account states
- Vehicle categories
- Document types
- Command types
- Application results
15. Example: Result Hierarchy
sealed interface Result
permits Success, Failure {
}
record Success(String message)
implements Result {
}
record Failure(String error)
implements Result {
}
The hierarchy clearly communicates that the result can be either successful or failed.
16. Processing a Sealed Result
static String process(Result result) {
return switch (result) {
case Success success ->
"Success: " + success.message();
case Failure failure ->
"Failure: " + failure.error();
};
}
17. Sealed Interfaces and Records
Records are implicitly final, which makes them convenient permitted implementations of sealed interfaces.
sealed interface Command
permits CreateUser, DeleteUser {
}
record CreateUser(
String name
) implements Command {
}
record DeleteUser(
long id
) implements Command {
}
18. Sealed Abstract Classes
A sealed class can also be abstract when the parent type should represent a common abstraction rather than be instantiated directly.
public abstract sealed class Employee
permits Developer, Manager {
}
final class Developer
extends Employee {
}
final class Manager
extends Employee {
}
19. Sealed Classes and Inheritance
Sealed classes do not remove inheritance. They make inheritance explicitly controlled.
sealed class Employee
permits Developer, Manager {
}
final class Developer
extends Employee {
}
final class Manager
extends Employee {
}
20. Advantages of Sealed Classes
- Controls inheritance explicitly.
- Improves domain modeling.
- Provides stronger compile-time guarantees.
- Works well with pattern matching.
- Can make switch expressions exhaustive.
- Makes permitted implementations easy to discover.
- Reduces unintended extension of important abstractions.
- Works well with records and modern Java features.
21. Limitations
- Not suitable when an inheritance hierarchy intentionally needs to remain open.
- Requires Java 17 or later as a standard language feature.
- Direct subclasses must follow the sealed, final, or non-sealed rules.
- Package and module rules must be respected.
- Can be unnecessary for simple class hierarchies.
22. Sealed vs final vs non-sealed
| Keyword | Inheritance Behavior |
|---|---|
final |
Stops inheritance completely. |
sealed |
Allows only explicitly permitted subtypes. |
non-sealed |
Reopens inheritance below that subtype. |
23. Example 🌍🫠
An order processing system may have a fixed set of order states.
sealed interface OrderStatus
permits Pending,
Confirmed,
Cancelled {
}
record Pending()
implements OrderStatus {
}
record Confirmed()
implements OrderStatus {
}
record Cancelled()
implements OrderStatus {
}
The application can then process the states using an exhaustive switch.
static String message(
OrderStatus status
) {
return switch (status) {
case Pending pending ->
"Order is pending";
case Confirmed confirmed ->
"Order is confirmed";
case Cancelled cancelled ->
"Order is cancelled";
};
}
24. Best Practices
- Use sealed types when the set of direct subtypes is intentionally closed.
- Use
finalfor leaf classes that should not be extended. - Use
non-sealedonly when reopening inheritance is intentional. - Combine sealed hierarchies with pattern matching when appropriate.
- Keep the permitted hierarchy small and meaningful.
- Use records for simple immutable implementations when suitable.
- Document why the hierarchy is intentionally restricted.
25. Interview Questions
permits clause lists the classes or interfaces
allowed to be direct subtypes of a sealed type.
final,
sealed, or non-sealed.
Summary
In this lesson, you learned:
- What sealed classes are
- The
sealedkeyword - The
permitsclause - final subclasses
- sealed subclasses
- non-sealed subclasses
- Sealed interfaces
- Sealed classes with records
- Pattern matching with sealed hierarchies
- Exhaustive switch expressions
- Real-world domain modeling
- Best practices and limitations