Introduction
Java’s evolution continues to introduce features that streamline coding and enhance security. Sealed Classes, introduced as a preview in Java 15 and finalized in Java 17, enable developers to control inheritance hierarchies more effectively. By explicitly specifying which classes can extend a given class, sealed classes bring more clarity, maintainability, and safety to object-oriented design.
In this article, we’ll explore sealed classes in-depth, covering their purpose, syntax, use cases, and best practices. Whether you’re a seasoned Java professional or exploring the latest Java features, understanding sealed classes will help you write more robust and maintainable code.
What Are Sealed Classes?
A sealed class is a new kind of class in Java that restricts which other classes or interfaces can extend or implement it. Unlike regular classes that allow any subclassing, sealed classes use explicit permissions to define an inheritance hierarchy.
Key Modifiers in Sealed Classes:
sealed
: Marks the class as sealed and allows specifying permitted subclasses.non-sealed
: Indicates that a subclass of a sealed class is open for extension.final
: Marks a subclass as the end of the hierarchy, disallowing further extension.
Why Use Sealed Classes?
- Control Over Class Hierarchies:
Developers can explicitly define which classes are allowed to extend a sealed class, reducing unintended subclassing. - Improved Security:
By limiting subclassing, sealed classes prevent unauthorized or potentially harmful extensions. - Better Maintainability:
Controlling inheritance ensures code adheres to intended design patterns. - Enhanced Switch Expressions:
Sealed classes work seamlessly with switch expressions, allowing exhaustive checks for all possible subclasses.
How to Implement Sealed Classes in Java
1. Declaring a Sealed Class
To declare a sealed class, use the sealed
modifier followed by a permits
clause that lists all permitted subclasses.
public sealed class Shape permits Circle, Rectangle, Triangle {}
This declaration specifies that only Circle
, Rectangle
, and Triangle
can extend the Shape
class.
2. Permitted Subclasses
Each permitted subclass must be declared as either:
final
: Prevents further subclassing.sealed
: Restricts its own hierarchy.non-sealed
: Opens itself for unrestricted extension.
Example:
public final class Circle extends Shape {}
public sealed class Rectangle extends Shape permits Square {}
public non-sealed class Triangle extends Shape {}
3. Using Sealed Classes in a Switch Expression
Sealed classes enhance switch expressions by enabling exhaustive checks for all permitted subclasses.
public String describeShape(Shape shape) {
return switch (shape) {
case Circle c -> "This is a Circle.";
case Rectangle r -> "This is a Rectangle.";
case Triangle t -> "This is a Triangle.";
};
}
Examples of Sealed Classes in Practice
Example 1: Sealed Class for Shapes
public sealed class Shape permits Circle, Rectangle, Triangle {}
public final class Circle extends Shape {
private final double radius;
public Circle(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
}
public final class Rectangle extends Shape {
private final double length, width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
public double getArea() {
return length * width;
}
}
Example 2: Sealed Class for Permissions
public sealed class User permits Admin, Guest {}
public final class Admin extends User {
public void manageSystem() {
System.out.println("Admin privileges granted.");
}
}
public non-sealed class Guest extends User {
public void browseContent() {
System.out.println("Guest privileges granted.");
}
}
Benefits of Using Sealed Classes
- Explicit Design Contracts:
Sealed classes clearly define the boundaries of an inheritance hierarchy. - Compiler Assistance:
Java compilers enforce the restrictions defined in sealed classes, reducing errors. - Optimized Runtime Performance:
The JVM can make optimizations by knowing all possible subclasses. - Compatibility with Pattern Matching:
Sealed classes integrate well with pattern matching, improving code readability. - Enhanced Debugging:
Restricted hierarchies make it easier to trace issues and understand the codebase.
Common Use Cases for Sealed Classes
- Domain-Specific Models:
In domain-driven design, sealed classes can define restricted sets of domain entities. - Error Handling:
Sealed classes can represent specific types of errors for precise exception handling. - State Machines:
Use sealed classes to represent a finite set of states in state machine implementations. - API Development:
Public APIs can restrict extensions to ensure security and stability. - Immutable Data Models:
Combine sealed classes with records for compact and immutable data representations.
Best Practices for Using Sealed Classes
- Keep Hierarchies Simple:
Avoid deep hierarchies to maintain clarity and readability. - Use Final Classes Wisely:
Finalize classes that are unlikely to need further extension. - Leverage Pattern Matching:
Combine sealed classes with pattern matching to write concise and robust code. - Document Hierarchies:
Clearly document the purpose of each class in the hierarchy for better maintainability. - Review for Compatibility:
Ensure older versions of Java are not required, as sealed classes are available from Java 15 onward.
External Resources to Learn More
- Java Sealed Classes Documentation
- Sealed Classes Tutorial on Baeldung
- Oracle Blog: Sealed Classes Deep Dive
FAQs on Sealed Classes in Java
- What are sealed classes in Java?
Sealed classes are a feature in Java that restrict which classes or interfaces can extend or implement them. - When were sealed classes introduced?
Sealed classes were introduced as a preview in Java 15 and finalized in Java 17. - Why should I use sealed classes?
They improve code maintainability, enforce design patterns, and enhance security by restricting class hierarchies. - Can a sealed class have unlimited subclasses?
No, only explicitly permitted subclasses can extend a sealed class. - What are the permitted modifiers for subclasses of a sealed class?
Subclasses must be marked asfinal
,sealed
, ornon-sealed
. - Can sealed classes implement interfaces?
Yes, a sealed class can implement one or more interfaces. - How do sealed classes work with switch expressions?
They enable exhaustive checks in switch expressions by covering all possible subclasses. - Are sealed classes backward-compatible?
No, they are available only in Java 15 and later versions. - What happens if I don’t use the
permits
clause?
The compiler will generate an error, as all subclasses must be explicitly listed. - Can a sealed class reside in a different package than its subclasses?
No, sealed classes and their permitted subclasses must reside in the same module or package.
Conclusion
Sealed classes are a significant addition to Java, providing developers with better control over inheritance hierarchies. By explicitly defining permitted subclasses, they reduce the risk of unintended extensions and enhance code maintainability. Whether you’re building APIs, domain models, or state machines, sealed classes bring clarity and safety to your designs.
If you’re using Java 17 or planning to upgrade, explore sealed classes to take your object-oriented programming skills to the next level. Combine them with modern Java features like pattern matching for even greater productivity and robustness.