Java 17 introduced sealed classes as part of the enhancements to the type system. Sealed classes allow developers to explicitly control which classes can extend or implement a class or interface, thereby achieving better type safety and making it easier to design domain-specific hierarchies. Here’s a guide on how to use sealed classes effectively:
What are Sealed Classes?
Sealed classes restrict which other classes or interfaces can extend or implement them. By using sealed classes, you can:
- Define a closed hierarchy of types where only a fixed set of subtypes is allowed.
- Ensure better maintainability and readability of your type hierarchy.
- Provide exhaustive handling for these types with features like
switchstatements.
The syntax revolves around the sealed, non-sealed, and final keywords.
Declaring and Using Sealed Classes
1. Declaration
To declare a sealed class:
- Use the
sealedmodifier. - Specify the permitted subclasses with the
permitsclause.
package org.kodejava.basic;
public sealed class Shape permits Circle, Rectangle, Square {
// Common properties and methods for all shapes
}
In this example:
Shapeis the sealed class.- Only
Circle,Rectangle, andSquareare allowed to extendShape.
2. Permitted Subclasses
Every subclass permitted by the sealed class must opt for one of the following:
final: The subclass cannot be further extended.non-sealed: The subclass can be extended by any other class.sealed: The subclass restricts its hierarchy further withpermits.
Examples:
package org.kodejava.basic;
// Final subclass (cannot have further subclasses)
public final class Circle extends Shape {
double radius;
public Circle(double radius) {
this.radius = radius;
}
}
package org.kodejava.basic;
// Sealed subclass with its own permitted subclasses
public sealed class Rectangle extends Shape permits RoundedRectangle {
double width, height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
}
package org.kodejava.basic;
// Non-sealed subclass (can have arbitrary subclasses)
public non-sealed class Square extends Shape {
double side;
public Square(double side) {
this.side = side;
}
}
package org.kodejava.basic;
// Permitted subclass of Rectangle
public final class RoundedRectangle extends Rectangle {
double cornerRadius;
public RoundedRectangle(double width, double height, double cornerRadius) {
super(width, height);
this.cornerRadius = cornerRadius;
}
}
Benefits of Sealed Classes
- Closed Type Hierarchies
Sealed classes provide an explicit way to define and restrict type hierarchies, avoiding unintended subclasses. -
Exhaustiveness in
switchStatements
When all subclasses of a sealed class are known, the compiler ensures exhaustiveness inswitchexpressions. This helps eliminate the possibility of missing a case.Example:
public double calculateArea(Shape shape) { return switch (shape) { case Circle c -> Math.PI * c.radius * c.radius; case RoundedRectangle rr -> rr.width * rr.height - (4 - Math.PI) * rr.cornerRadius * rr.cornerRadius / 4; case Rectangle r -> r.width * r.height; case Square s -> s.side * s.side; default -> throw new IllegalStateException("Unexpected value: " + shape); }; }If you later add a new subclass to
Shape, the compiler will generate an error until you update the switch statement accordingly. -
Immutability and Security
By marking direct subclasses asfinalor controlling further inheritance (e.g., viasealedvs.non-sealed), you ensure immutability in specific contexts and prevent unintended behavior caused by subclassing.
Practical Use Cases for Sealed Classes
-
Domain Modelling
Example: A sealed classPaymentcan have subclasses forCreditCardPayment,BankTransfer, andCryptoPayment.public sealed class Payment permits CreditCardPayment, BankTransfer, CryptoPayment { // Common payment attributes } public final class CreditCardPayment extends Payment { // Credit card specific fields } public final class BankTransfer extends Payment { // Bank transfer specific fields } public final class CryptoPayment extends Payment { // Crypto payment specific fields } - Compiler Assistance for Type-Safe Code
The sealed hierarchy ensures that when you process these types (e.g., withswitchor polymorphic methods), the compiler helps enforce exhaustive handling.
Key Points to Remember
- You must list all permitted subclasses explicitly using the
permitsclause. - All subclasses of a sealed class must be declared in the same module or package as the sealed class (enhanced encapsulation).
sealed,non-sealed, andfinaldefine the inheritance relation for permitted subclasses.
Summary
Sealed classes are a powerful tool in Java 17 for creating controlled and predictable type hierarchies. They help enforce constraints at compile-time, reduce runtime errors, and assist developers in creating clean, maintainable, and type-safe code. Use them effectively to create robust domain models and application logic.
