Last Updated: January 3, 2026
Sealed classes are a powerful feature introduced in Java 15 as a preview, and solidified in Java 17. They allow you to define a class with a limited set of subclasses.
This concept tackles the common issue of uncontrolled inheritance, where any class can extend any other class, potentially leading to unmanageable code.
By sealing a class, you can ensure that only specific known subclasses can extend it, enhancing both maintainability and security in your codebase.
Let's dive into the world of sealed classes and explore their syntax, practical use cases, and nuances.
A sealed class is defined using the sealed keyword. This designation indicates that the class can only be subclassed by specified classes or interfaces. To declare a sealed class in Java, you also need to specify which classes are allowed to extend it using the permits clause.
Here's a basic example:
In this example, Shape is a sealed class that can only be extended by the Circle and Rectangle classes.
Let’s dig deeper into how you can create and utilize sealed classes effectively.
Here’s a more detailed example illustrating how to define a sealed class and its subclasses:
In this example, Vehicle is a sealed class with Car and Truck as its permitted subclasses. Notice how both subclasses are declared as final. This means they cannot be further extended, which is a common practice in sealed class hierarchies to maintain control.
Permitted subclasses can be:
Here is how you might implement all three options:
permits ClauseThe permits clause clearly shows which classes can extend the sealed class. This explicit relationship is crucial for understanding the code structure.
Sealed classes shine in scenarios where you want to control the types of objects that can be created. Here are a few real-world applications:
In domain-driven design, sealed classes can represent a finite set of types. For example, consider an application that processes different payment methods:
By using sealed classes, you ensure that all payment types are predefined, reducing the risk of errors introduced by unexpected implementations.
Sealed classes can also model state machines effectively. Each state can be a subclass that knows how to handle transitions:
This approach clarifies which states exist and prevents unauthorized states from being introduced.
Just like classes, you can also create sealed interfaces. This can be useful in scenarios where you want to define a contract with a limited set of implementations.
Working with sealed classes can bring some complexities. It’s essential to be aware of edge cases that can cause confusion or errors.
You can have sealed classes nested within other classes. However, the outer class must also follow the rules of sealing if it’s declared as sealed.
If a sealed class is defined but has no permitted subclasses, it cannot be instantiated directly, which might lead to confusion.
Always ensure that your sealed classes have at least one permitted subclass. Otherwise, it could lead to unexpected situations in your code.
Using reflection with sealed classes can be tricky, as it may expose all subclasses, potentially violating the intent of sealing. When using reflection, ensure you maintain control over what is exposed.
Sealed classes and interfaces provide a robust way to create controlled hierarchies in your Java applications. Here are some best practices to keep in mind:
final, non-sealed, or sealed for subclasses as per your design requirement to maintain a clear and predictable structure.Sealed classes are a game-changer for managing class hierarchies effectively. They not only improve the design of your application but also enhance maintainability and readability.