Sealed classes
Sealed class (Java 17) — класс, который явно задаёт список своих наследников. Раньше любой класс можно было наследовать откуда угодно — или закрыть через final (тогда наследников вообще нет). Sealed — это промежуток: наследники есть, но только строго определённые.
sealed interface Shape permits Circle, Rectangle, Triangle {}
final class Circle implements Shape { double radius; }
final class Rectangle implements Shape { double width, height; }
final class Triangle implements Shape { double base, height; }
Ключевое слово permits — явный список разрешённых наследников. Попытка создать другой наследник даст ошибку компиляции.
Зачем это нужно:
// Компилятор знает ВСЕ реализации Shape
// → switch может проверить, что вы не забыли ни одну
double area(Shape s) {
return switch (s) {
case Circle c -> Math.PI * c.radius * c.radius;
case Rectangle r -> r.width * r.height;
case Triangle t -> 0.5 * t.base * t.height;
// default не нужен — компилятор видит что все случаи покрыты
};
}
Копнуть глубже
Три варианта для разрешённых подклассов:
final— нельзя наследовать дальше (терминальный узел)sealed— можно наследовать, но снова сpermits(дерево типов)non-sealed— снимает ограничение, любой может наследовать
sealed class Vehicle permits Car, Truck, SpecialVehicle {}
final class Car extends Vehicle {}
final class Truck extends Vehicle {}
non-sealed class SpecialVehicle extends Vehicle {} // от неё — без ограничений
Sealed + record = алгебраические типы данных. Комбинация позволяет моделировать типы как в функциональных языках:
sealed interface Result<T> permits Result.Ok, Result.Err {
record Ok<T>(T value) implements Result<T> {}
record Err<T>(String msg) implements Result<T> {}
}
Result<User> result = findUser(id);
String message = switch (result) {
case Result.Ok<User> ok -> "Нашли: " + ok.value().name();
case Result.Err<User> err -> "Ошибка: " + err.msg();
};
Это чище, чем исключения для ожидаемых ошибок, и компилятор проверяет что вы обработали оба случая.
Sealed классы и рефлексия. Class.permittedSubclasses() — можно получить список разрешённых наследников в рантайме. Фреймворки (Jackson, Hibernate) используют это для сериализации иерархий типов без дополнительных аннотаций.
• как sealed + switch позволяют компилятору проверить полноту обработки (если дошёл до 2-го слоя).