Introduction
Java 15 introduced sealed classes as a preview feature, and they became a standard feature in Java 17. Sealed classes provide a way to control the inheritance hierarchy by explicitly specifying which classes are permitted to extend or implement them.
Key Points:
- Controlled Inheritance: Sealed classes restrict which classes or interfaces can extend or implement them.
- Improved Design: Enables more predictable class hierarchies, improving design and maintainability.
- Flexibility: Works seamlessly with non-sealed and final classes to provide various inheritance combinations.
- Pattern Matching: Complements the use of pattern matching and type checks for exhaustive handling of subclass types.
Syntax of Sealed Classes
A sealed class is defined using the sealed keyword, followed by a permits clause that lists the permitted subclasses.
Basic Syntax
public sealed class ClassName permits Subclass1, Subclass2 {
// Class body
}
- ClassName: The name of the sealed class.
- permits: A clause listing the classes that are allowed to extend the sealed class.
- Subclass1, Subclass2: The permitted subclasses that can extend the sealed class.
Sealed Interfaces
Sealed classes can be used with interfaces as well. A sealed interface restricts which classes or interfaces can implement or extend it.
Syntax for Sealed Interfaces
public sealed interface InterfaceName permits ImplementingClass1, ImplementingClass2 {
// Interface body
}
Subclassing Sealed Classes
Permitted subclasses of a sealed class must specify one of the following modifiers:
final: Indicates that the subclass cannot be extended further.sealed: Continues the sealed hierarchy by specifying its own permitted subclasses.non-sealed: Allows unrestricted subclassing beyond the sealed hierarchy.
Examples
Let’s explore examples of sealed classes and interfaces and how they can be used to create controlled inheritance hierarchies.
Example 1: Sealed Class with Final Subclasses
public sealed class Shape permits Circle, Rectangle {}
public final class Circle extends Shape {
// Circle-specific implementation
}
public final class Rectangle extends Shape {
// Rectangle-specific implementation
}
Explanation:
- Sealed Class: The
Shapeclass is sealed, restricting its subclasses toCircleandRectangle. - Final Subclasses: Both
CircleandRectangleare final, indicating that they cannot be further subclassed.
Example 2: Sealed Class with Non-Sealed Subclass
public sealed class Shape permits Circle, Triangle {}
public final class Circle extends Shape {
// Circle-specific implementation
}
public non-sealed class Triangle extends Shape {
// Triangle-specific implementation
}
// Unrestricted subclassing for Triangle
public class RightAngledTriangle extends Triangle {
// Specific implementation for right-angled triangle
}
Explanation:
- Non-Sealed Subclass: The
Triangleclass is non-sealed, allowing unrestricted subclassing beyond the sealed hierarchy. - Further Subclassing:
RightAngledTriangleextendsTrianglewithout any restrictions.
Example 3: Sealed Interfaces
public sealed interface Vehicle permits Car, Truck {}
public final class Car implements Vehicle {
// Car-specific implementation
}
public non-sealed class Truck implements Vehicle {
// Truck-specific implementation
}
public class HeavyTruck extends Truck {
// HeavyTruck-specific implementation
}
Explanation:
- Sealed Interface: The
Vehicleinterface is sealed, restricting its implementers toCarandTruck. - Final Implementation:
Caris final, meaning it cannot be further extended. - Non-Sealed Implementation:
Truckis non-sealed, allowing additional subclasses likeHeavyTruck.
Benefits of Sealed Classes
- Controlled Inheritance: Sealed classes provide a way to control and restrict class hierarchies, ensuring that only specified classes can extend them.
- Improved Code Design: By enforcing constraints on inheritance, sealed classes help improve code design, making it more predictable and easier to maintain.
- Exhaustive Pattern Matching: Sealed classes complement pattern matching, allowing for exhaustive handling of subclasses in switch expressions or
instanceofchecks.
Common Use Cases
Use Case 1: Domain Modeling
Sealed classes are useful for modeling domains where a fixed set of subclasses represent all possible variations.
public sealed class PaymentMethod permits CreditCard, DebitCard, PayPal {}
public final class CreditCard extends PaymentMethod {
// CreditCard-specific implementation
}
public final class DebitCard extends PaymentMethod {
// DebitCard-specific implementation
}
public final class PayPal extends PaymentMethod {
// PayPal-specific implementation
}
Use Case 2: Restricting API Usage
Sealed classes can be used to restrict the implementation of APIs, allowing only specific classes to be used in certain contexts.
public sealed interface Plugin permits AudioPlugin, VideoPlugin {}
public final class AudioPlugin implements Plugin {
// AudioPlugin-specific implementation
}
public final class VideoPlugin implements Plugin {
// VideoPlugin-specific implementation
}
Use Case 3: Exhaustive Pattern Matching
Sealed classes work well with pattern matching to ensure all possible cases are handled exhaustively.
public sealed class Shape permits Circle, Square, Triangle {}
public final class Circle extends Shape {}
public final class Square extends Shape {}
public final class Triangle extends Shape {}
public class ShapeAreaCalculator {
public double calculateArea(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Square s -> s.side() * s.side();
case Triangle t -> 0.5 * t.base() * t.height();
};
}
}
Explanation:
- Exhaustive Handling: The
switchexpression handles all possible subclasses ofShape, ensuring exhaustive pattern matching.
Conclusion
Sealed classes in Java control inheritance hierarchies, improve code design, and enable exhaustive pattern matching. By specifying which classes are allowed to extend or implement a sealed class or interface, developers can create more predictable and maintainable code.
Summary:
- Controlled Inheritance: Sealed classes restrict which classes can extend them, providing controlled inheritance hierarchies.
- Improved Design: Sealed classes enhance code design by enforcing constraints on class hierarchies.
- Pattern Matching: Complement pattern matching by enabling exhaustive handling of subclass types.
- Flexibility: Work seamlessly with final and non-sealed classes to provide various inheritance combinations.
By leveraging sealed classes, developers can create more robust and maintainable Java applications, ensuring that class hierarchies align with intended design constraints.