Sealed classes are an enhancement to the language included with LTS version 17 (included since version 16) of Java. Sealed classes or interfaces allow us to restrict what other classes and interfaces can extend from them.
Note: The sealed classes don’t have as goal to replace the final
keyword
The goal of sealed classes or interfaces is to provide a way to allow a class to be widely accessible but not widely extensible. These can be extended or implemented only by those classes and interfaces that are explicitly allowed.
How to use it?
A class or interface is sealed by applying the sealed
modifier to its definition, after any extends or implements the permits
clause must be added followed by the classes that we allow to be extended.
public abstract sealed class Shape permits Circle, Rectangle, Square, WeirdShape { ... }
Then, in the implementations (permitted classes) we must extend the sealed class.
public final class Circle extends Shape { ... } public final class Rectangule extends Shape { ... } public final class Square extends Shape { ... } public final class WeirdShape extends Shape { ... }
Permitted Sub-classes and its rules
Each of the “permitted” sub-class must use a modifier to describe how to propagate the sealing initiated by its superclass:
- A “permitted” subclass might apply the
final
modifier to avoid spreading further. - A “permitted” subclass might apply the
sealed
modifier followed by the clausepermits
to extend to other sub-classes in its hierarchy. - A “permitted” sub-class might apply the
non-sealed
modifier, so it reverts on its own hierarchy the “sealed” by the super-class and opening to extension to other unknown classes by the super class.
Let’s see some examples:
public abstract sealed class Shape permits Circle, Rectangle, Square, WeirdShape { ... } public final class Circle extends Shape { ... } public sealed class Rectangle extends Shape permits TransparentRectangle, FilledRectangle { ... } public final class TransparentRectangle extends Rectangle { ... } public final class FilledRectangle extends Rectangle { ... } public final class Square extends Shape { ... } public non-sealed class WeirdShape extends Shape { ... }
In summary, at least one of the final
, sealed
or non-sealed
modifiers must be applied to the permitted sub-classes.
Sealed Interfaces
As it has been mentioned previously, interfaces can also be sealed in the same way as classes. You must follow the same rules as the sealed classes, apply the sealed modifier and follow the permits clause with the list of allowed implementations.
public sealed interface Expr permits ConstantExpr, PlusExpr, TimesExpr, NegExpr { ... } public final class ConstantExpr implements Expr { ... } public final class PlusExpr implements Expr { ... } public final class TimesExpr implements Expr { ... } public final class NegExpr implements Expr { ... }
Sealing and record classes
The record
classes introduced in Java 15 are implicitly final so it is consistent with the rules for the allowed subclasses mentioned above.
package com.example.expression; public sealed interface Expr permits ConstantExpr, PlusExpr, TimesExpr, NegExpr { ... } public record ConstantExpr(int i) implements Expr { ... } public record PlusExpr(Expr a, Expr b) implements Expr { ... } public record TimesExpr(Expr a, Expr b) implements Expr { ... } public record NegExpr(Expr e) implements Expr { ... }
Conclusions
Definitely the sealed classes and interfaces are a great inclusion to the Java language, allowing to cover some limitations in the language for the creators of APIs and libraries, where in some cases we want to restrict the extensibility of our classes without limiting their accessibility.
This new feature opens the doors to incorporate many other future features to the language, such as the switch
pattern matching
that is being worked on in JEP 406