Расскажите про records, sealed-классы и pattern matching. Какие задачи они решают вместе?
Короткий ответ
- record — неизменяемый носитель данных с готовыми equals/hashCode/toString
- Компоненты record финальны, наследоваться от классов нельзя
- sealed ограничивает список наследников через permits
- switch с pattern matching разбирает типы и деконструирует record
- sealed + switch = исчерпывающая проверка без default
- Вместе это алгебраические типы данных для моделирования домена
Records и sealed-иерархии со switch pattern matching дают компактное и типобезопасное моделирование доменных состояний.
Как сказать вслух
пример ответаRecord — это класс для данных: я перечисляю компоненты, а конструктор, геттеры, equals, hashCode и toString генерируются сами, и объект неизменяемый. Sealed-класс или интерфейс жёстко ограничивает, кто может его наследовать. А pattern matching в switch позволяет разобрать объект по типу и сразу вытащить поля. Вместе это удобно для моделирования состояний: компилятор сам проверит, что я обработал все варианты, и сломает сборку, если добавится новый.
Подробный ответ
Основной ответ
record (с Java 16) — финальный класс с финальными компонентами: канонический конструктор, методы доступа, equals/hashCode/toString генерируются; инварианты проверяют в компактном конструкторе. Это убирает шаблонный код DTO и value-объектов. sealed (с Java 17) ограничивает наследников списком permits; наследник обязан быть final, sealed или non-sealed. Pattern matching: instanceof с паттерном типа, switch по типам (Java 21) и record patterns — деконструкция вложенных структур: case Circle(Point(var x, var y), var r). Ключевая синергия: switch по sealed-иерархии проверяется на исчерпываемость — забытый вариант даёт ошибку компиляции, default не нужен и даже вреден. Это приближает Java к алгебраическим типам данных: Result = Ok | Error, события, состояния заказа — вместо enum с полями-«на все случаи» или иерархий с visitor.
Ключевые моменты
- record. Неизменяемый носитель данных; валидация — в компактном конструкторе; изменение — через производящие методы (with-подход).
- sealed. Закрытая иерархия: компилятор знает всех наследников, что и делает возможной исчерпывающую проверку.
- Record patterns. Деконструкция в switch/instanceof, включая вложенные records и var для компонентов.
- Исчерпываемость. Без default добавление нового наследника ломает компиляцию всех switch — ошибки находят на сборке.
Практический контекст
В проде records используют для DTO, событий, ключей кэшей, результатов методов (вместо null или исключений — sealed Result). Интервьюер проверяет знание современной Java: когда record нельзя (нужна изменяемость, наследование, JPA-сущности — прокси и ленивость с records не дружат) и зачем не ставить default в switch по sealed. Хорошо упомянуть, что record может реализовывать интерфейсы и иметь статические фабрики.
Частые ошибки
- Используют record для JPA-сущностей, где нужны прокси и изменяемое состояние
- Добавляют default в switch по sealed-иерархии, теряя проверку исчерпываемости
- Считают record полностью неизменяемым, забывая про изменяемое содержимое компонентов-коллекций