The 30 second answer
Sealed classes, final in Java 17, let a class or interface decide exactly which types may extend it, using a permits list. Each allowed subtype must say whether it is final, sealed again, or non-sealed. This models a closed set of cases, like payment types. The big win is that the compiler can check a switch over a sealed type covers every case, without a default.
sealed interface Shape permits Circle, Square, Rect {}
record Circle(double r) implements Shape {}
record Square(double side) implements Shape {}
non-sealed class Rect implements Shape { double w, h; } // open again for extension
double area(Shape s) {
return switch (s) { // Java 21: no default needed, all permitted types covered
case Circle c -> Math.PI * c.r() * c.r();
case Square q -> q.side() * q.side();
case Rect r -> r.w * r.h;
};
}Rules: permitted subclasses must be in the same module (or the same package, if in the unnamed module). If they're all in the same file, permits can be left out. Records are implicitly final, so they fit naturally.
Compared with an enum, each case can carry different data. Compared with an open hierarchy, adding a new subtype makes non-exhaustive pattern switches fail to compile, which shows you every place to update. (Exhaustive pattern switches over sealed types arrived with pattern matching for switch, final in Java 21.)
Sealed classes were previewed in Java 15 and 16 and became final in Java 17.
Sources: JEP 409: Sealed Classes, JLS 8.1.1.2 sealed, non-sealed, and final Classes