В багтрекере OpenJDK появился новый черновик JEP, в котором специфицируются новые языковые конструкции Java: записи и запечатанные типы. Идея их введения была предложена полтора года назад и была описана в исследовательском документе. Наконец, после долгих дискуссий и экспериментов она достигла достаточного уровня зрелости для того, чтобы с большой уверенностью сказать, что она будет реализована в одной из будущих версий Java.
Записи (records) позволят очень компактно объявлять классы, которые являются простыми носителями данных:
record Range(int lo, int hi) { public Range { if (lo > hi) { throw new IllegalArgumentException( String.format("(%d,%d)", lo, hi)); } } }
Сейчас такой класс Range реализуется чрезвычайно многословно, поскольку автору кода необходимо самому написать конструктор, методы доступа к полям, equals()
, hashCode()
и toString()
. Это может раздражать и читателя, ведь эти члены не содержат в себе никакой особо полезной смысловой нагрузки. В записях же все эти члены генерируются компилятором автоматически и поэтому экономят время и строки кода.
Записи имеют следующие особенности:
- Они иммутабельны (все поля становятся
final
). - От них нельзя отнаследоваться (они являются
final
). - Они сами не могут отнаследоваться от классов.
- Если записи являются вложенными, то они являются
static
. - Они не могут содержать дополнительных полей.
- Они могут содержать явный конструктор по умолчанию (в котором можно, например, сделать валидацию полей).
В остальном записи ничем не отличаются от обычных классов: они могут наследоваться от интерфейсов, содержать методы, конструкторы, статические инициализаторы, вложенные типы, иметь аннотации и т.д.
Вторая конструкция, тесно связанная с записями – это запечатанные типы (sealed types). Такие типы накладывают ограничение на типы, которые могут от них наследоваться. Делается это одним из двух способов. Первый способ – с помощью ключевого слова permits
:
sealed interface Node permits A, B, C { ... }
В этом случае от интерфейса Node не могут быть отнаследованы никакие другие типы кроме A, B и C.
Второй способ – без использования ключевого слова permits. Тогда список наследников будет ограничен только теми типами, которые находятся в той же единице компиляции.
Запечатанные типы имеют следующие особенности:
- От них не могут быть отнаследованы анонимые классы и лямбды.
- Их абстрактные подтипы неявно являются запечатанными (но можно переопределить с помощью ключевого слова
non-sealed
). - Их конкретные подтипы неявно являются
final
.
Записи и запечатанные типы вместе являются реализацией концепции алгебраических типов данных в Java. Поскольку для комфортной работы с алгебраическими типами данных также нужен паттерн-матчинг, то в будущем записи предлагается снабдить деконструирующими паттернами, а для запечатанных типов разработать механизм проверки exhaustiveness в switch
-выражениях.