Sealed Classes in Java
What
- Sum types (a subset of Algebraic data types)
- A way to restrict class hierarchy downstream by specifying which classes can extend a class.
Why
- Exhaustive pattern matching
- (Sort of) hierarchical Enums
- Lock down/selectively permit hierarchy
- More expressive type-system than access-control system
Constraints
- The sealed class and its permitted subclasses must belong to the same module, and, if declared in an unnamed module, to the same package.
- Every permitted subclass must directly extend the sealed class.
- Every permitted subclass must use a modifier to describe how it propagates the sealing initiated by its superclass: - A permitted subclass may be declared final to prevent its part of the class hierarchy from being extended further. (Record classes are implicitly declared final.)
- A permitted subclass may be declared sealed to allow its part of the hierarchy to be extended further than envisaged by its sealed superclass, but in a restricted fashion.
- A permitted subclass may be declared non-sealed so that its part of the hierarchy reverts to being open for extension by unknown subclasses. A sealed class cannot prevent its permitted subclasses from doing this. (The modifier non-sealed is the first hyphenated keyword proposed for Java.)
 
How
- Basic syntax
1package com.example.geometry;
2
3public abstract sealed class Shape
4    permits com.example.polar.Circle,
5            com.example.quad.Rectangle,
6            com.example.quad.simple.Square { ... }- Extensive example
 1package com.example.geometry;
 2
 3public abstract sealed class Shape
 4    permits Circle, Rectangle, Square, WeirdShape { ... }
 5
 6public final class Circle extends Shape { ... }
 7
 8public sealed class Rectangle extends Shape
 9    permits TransparentRectangle, FilledRectangle { ... }
10public final class TransparentRectangle extends Rectangle { ... }
11public final class FilledRectangle extends Rectangle { ... }
12
13public final class Square extends Shape { ... }
14
15public non-sealed class WeirdShape extends Shape { ... }- Sealed interfaces
1package com.example.expression;
2
3public sealed interface Expr
4    permits ConstantExpr, PlusExpr, TimesExpr, NegExpr { ... }
5
6public final class ConstantExpr implements Expr { ... }
7public final class PlusExpr     implements Expr { ... }
8public final class TimesExpr    implements Expr { ... }
9public final class NegExpr      implements Expr { ... }- Another interface example
1interface I {}
2sealed class C permits D, E {}
3non-sealed class D extends C {}
4final class E extends C {}
5
6void test (C c) {
7    if (c instanceof I)
8        System.out.println("It's an I");
9}- Sealing and record classes
1package com.example.expression;
2
3public sealed interface Expr
4    permits ConstantExpr, PlusExpr, TimesExpr, NegExpr { ... }
5
6public record ConstantExpr(int i)       implements Expr { ... }
7public record PlusExpr(Expr a, Expr b)  implements Expr { ... }
8public record TimesExpr(Expr a, Expr b) implements Expr { ... }
9public record NegExpr(Expr e)           implements Expr { ... }- Sealed classes and pattern matching
1Shape rotate(Shape shape, double angle) {
2    return switch (shape) {   // pattern matching switch
3        case Circle c    -> c;
4        case Rectangle r -> shape.rotate(angle);
5        case Square s    -> shape.rotate(angle);
6        // no default needed!
7    }
8}- Java grammar after extension
 1NormalClassDeclaration:
 2  {ClassModifier} class TypeIdentifier [TypeParameters]
 3    [Superclass] [Superinterfaces] [PermittedSubclasses] ClassBody
 4
 5ClassModifier:
 6  (one of)
 7  Annotation public protected private
 8  abstract static sealed final non-sealed strictfp
 9
10PermittedSubclasses:
11  permits ClassTypeList
12
13ClassTypeList:
14  ClassType {, ClassType}