Sealed-типы
sealed class/interface (Java 17) ограничивает список наследников через permits: только перечисленные типы могут расширять/реализовывать его. Каждый разрешённый подтип обязан быть final, sealed или non-sealed (явно «открыт снова»).
Зачем: закрытая иерархия домена (алгебраический тип) — компилятор ЗНАЕТ полный набор вариантов. Это даёт исчерпывающие проверки и точную модель: PaymentResult — это ровно {Ok, Failed}, и третьего не дано.
sealed interface PaymentResult permits PaymentOk, PaymentFailed {}
record PaymentOk(String id) implements PaymentResult {}
record PaymentFailed(String reason) implements PaymentResult {}
Pattern matching в instanceof
Pattern matching for instanceof убирает ручной cast: if (o instanceof String s) сразу связывает переменную s нужного типа, если проверка прошла.
Работает flow scoping: переменная-паттерн видна только там, где компилятор доказал её тип (например, в then-ветке, или после раннего return в else). Это и короче, и безопаснее, чем instanceof + явный cast.
if (obj instanceof String s && !s.isBlank()) {
use(s); // s уже String, без приведения
}
switch с паттернами и исчерпываемость
switch умеет ветвиться по типу: case PaymentOk ok -> …, case PaymentFailed f -> …. Для sealed-типа компилятор проверяет ИСЧЕРПЫВАЕМОСТЬ: если покрыты все разрешённые варианты, default не нужен. И наоборот — добавите новый подтип в sealed-иерархию, и компилятор заставит обработать его во всех таких switch (ошибка компиляции вместо тихого пропуска).
Условия уточняют guarded-паттерны: case Integer i when i > 0. Это превращает switch в полноценный разбор вариантов, как match в функциональных языках.
String describe(PaymentResult r) {
return switch (r) { // default не нужен — sealed исчерпан
case PaymentOk ok -> "ok: " + ok.id();
case PaymentFailed f -> "fail: " + f.reason();
};
}
Деконструкция записей
Record patterns (Java 21) распаковывают запись прямо в паттерне: case Point(int x, int y) -> … достаёт компоненты в переменные. Деконструкция вкладывается: case Line(Point(var x1, var y1), Point(var x2, var y2)).
Связка sealed + record + switch с деконструкцией — это и есть алгебраические типы данных в Java: компактный, типобезопасный и исчерпывающий разбор формы данных.
null в switch
Историческая ловушка: обычный switch бросал NullPointerException на null. В switch с паттернами это лечится явной веткой case null (иначе поведение прежнее).
Как отвечать на собеседовании
Объясните sealed как закрытую иерархию (компилятор знает все варианты → исчерпываемость), pattern matching в instanceof (без cast, flow scoping) и switch по типам с проверкой полноты для sealed, и покажите деконструкцию record как вершину — алгебраические типы. Подчеркните выгоду: добавление варианта ловится компилятором во всех switch.