bhankas

Sealed Classes in Java

What

Why

  • Exhaustive pattern matching
  • (Sort of) hierarchical Enums
  • Lock down/selectively permit hierarchy
  • More expressive type-system than access-control system

Constraints

  1. The sealed class and its permitted subclasses must belong to the same module, and, if declared in an unnamed module, to the same package.
  2. Every permitted subclass must directly extend the sealed class.
  3. 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}