🎓 A1Learner
🔍
0 of 30 lessons completed

Java Sealed Classes

A sealed class or interface limits which classes may extend it, naming them in a permits list. The compiler knows the whole family, so a switch needs no default branch.

A switch with no default covers the family

Shape permits Circle and Square only. The switch covers both and prints one area per shape.

public class Main {
  static String describe(Shape shape) {
    return switch (shape) {
      case Circle c -> "Circle area " + String.format("%.2f", Math.PI * c.radius() * c.radius());
      case Square s -> "Square area " + String.format("%.2f", s.side() * s.side());
    };
  }
  public static void main(String[] args) {
    System.out.println(describe(new Circle(2)));
    System.out.println(describe(new Square(3)));
  }
}
sealed interface Shape permits Circle, Square {
}
record Circle(double radius) implements Shape {
}
record Square(double side) implements Shape {
}

Deleting one branch breaks the switch

The same switch loses its Square branch. The compiler refuses it, and the refusal prints below.

public class Main {
  static String describe(Shape shape) {
    return switch (shape) {
      case Circle c -> "Circle area " + String.format("%.2f", Math.PI * c.radius() * c.radius());
    };
  }
  public static void main(String[] args) {
    System.out.println(describe(new Circle(1)));
  }
}
sealed interface Shape permits Circle, Square {
}
record Circle(double radius) implements Shape {
}
record Square(double side) implements Shape {
}

A class outside the list is refused

Triangle tries to join the family from outside the permits list. The compiler refuses it, and the refusal prints below.

public class Main {
  public static void main(String[] args) {
    System.out.println("family of two");
  }
}
sealed interface Shape permits Circle, Square {
}
record Circle(double radius) implements Shape {
}
record Square(double side) implements Shape {
}
class Triangle implements Shape {
}

Example

import java.util.List;
public class Main {
  static String describe(Shape shape) {
    return switch (shape) {
      case Circle c -> "Circle area " + String.format("%.2f", Math.PI * c.radius() * c.radius());
      case Square s -> "Square area " + String.format("%.2f", s.side() * s.side());
    };
  }
  public static void main(String[] args) {
    List<Shape> shapes = List.of(new Circle(1), new Square(4));
    for (Shape shape : shapes) {
      System.out.println(describe(shape));
    }
  }
}
sealed interface Shape permits Circle, Square {
}
record Circle(double radius) implements Shape {
}
record Square(double side) implements Shape {
}

Key points

  • The permits clause lists every class allowed in the family.
  • Every permitted subclass is final, sealed or non-sealed.
  • A record is final, so records pair well with sealed types.
  • A switch over a sealed type must cover every permitted class.
  • A class outside the permits list cannot join the family.

In this example, a list holds one Circle and one Square, and the switch prints each area with no default branch.

Note

Sealing closes a family on purpose, and a new member needs a permits change.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

Why can the switch over Shape leave out the default branch?