JEP 305: Pattern Matching for instanceof (Preview)
Pattern Matching (сопоставление с образцом) для instanceof, версия Preview (предварительная версия)
| Автор | Brian Goetz |
| Ответственный | Gavin Bierman |
| Тип | Feature |
| Область | SE |
| Статус | Closed / Delivered |
| Выпуск | 14 |
| Компонент | specification / language |
| Обсуждение | amber dash dev at openjdk dot java dot net |
| Связан с | JEP 375: Pattern Matching for instanceof (Second Preview) |
| JEP 394: Pattern Matching for instanceof | |
| Рецензенты | Mark Reinhold |
| Создан | 2017/05/30 19:48 |
| Обновлён | 2021/08/28 00:17 |
| Задача | 8181287 |
Аннотация
Расширить язык программирования Java возможностью Pattern Matching для оператора instanceof. Pattern Matching позволяет выразить распространённую в программах логику, а именно условное извлечение компонентов из объектов, более кратко и безопасно. Это возможность языка в статусе Preview в JDK 14.
Мотивация
Почти в каждой программе есть логика, которая проверяет, имеет ли выражение определённый тип или структуру, а затем при выполнении условия извлекает компоненты его состояния для дальнейшей обработки. Например, всем Java-программистам знакома идиома «instanceof и приведение типа»:
if (obj instanceof String) {
String s = (String) obj;
// use s
}
Здесь происходят три вещи: проверка (является ли obj объектом String?), преобразование (приведение obj к String) и объявление новой локальной переменной (s), чтобы можно было использовать строковое значение. Этот шаблон прост и понятен всем Java-программистам, но неоптимален по нескольким причинам. Он утомителен: выполнять и проверку типа, и приведение не должно быть нужно (что ещё делать после проверки instanceof?). Этот шаблонный код — в частности, три упоминания типа String — заслоняет более важную логику, которая следует за ним. Но главное, повторение создаёт возможности для того, чтобы в программы незаметно проникли ошибки.
Вместо того чтобы прибегать к частным решениям, мы считаем, что Java пора принять Pattern Matching. Pattern Matching позволяет кратко выразить желаемую «форму» объекта (шаблон), а различным операторам и выражениям — проверять эту «форму» на своих входных данных (сопоставление). Многие языки, от Haskell до C#, приняли Pattern Matching за краткость и безопасность.
Описание
Шаблон — это сочетание (1) предиката, который можно применить к цели, и (2) набора переменных привязки, которые извлекаются из цели, только если предикат успешно к ней применяется.
Шаблон проверки типа состоит из предиката, задающего тип, и одной переменной привязки.
Оператор instanceof (JLS 15.20.2) расширяется так, чтобы принимать шаблон проверки типа, а не только тип. В коде ниже фраза String s является шаблоном проверки типа:
if (obj instanceof String s) {
// can use s here
} else {
// can't use s here
}
Оператор instanceof «сопоставляет» цель obj с шаблоном проверки типа следующим образом: если obj является экземпляром String, то он приводится к String и присваивается переменной привязки s. Переменная привязки находится в области видимости в ветви true оператора if и не находится в области видимости в ветви false оператора if.
Область видимости переменной привязки, в отличие от области видимости локальной переменной, определяется семантикой содержащих её выражений и операторов. Например, в этом коде:
if (!(obj instanceof String s)) {
.. s.contains(..) ..
} else {
.. s.contains(..) ..
}
s в ветви true ссылается на поле объемлющего класса, а s в ветви false ссылается на переменную привязки, введённую оператором instanceof.
Когда условие оператора if становится сложнее одного instanceof, область видимости переменной привязки соответственно расширяется. Например, в этом коде:
if (obj instanceof String s && s.length() > 5) {.. s.contains(..) ..}
переменная привязки s находится в области видимости в правой части оператора &&, а также в ветви true. (Правая часть вычисляется, только если instanceof завершился успешно и выполнил присваивание s.) С другой стороны, в этом коде:
if (obj instanceof String s || s.length() > 5) {.. s.contains(..) ..}
переменная привязки s не находится в области видимости ни в правой части оператора ||, ни в ветви true. (s в этих местах ссылается на поле объемлющего класса.)
Поведение instanceof, когда цель равна null, не меняется. То есть шаблон сопоставится, а s получит значение, только если obj не равен null.
Использование Pattern Matching в instanceof должно резко сократить общее количество явных приведений типов в Java-программах. Кроме того, шаблоны проверки типа особенно полезны при написании методов проверки на равенство. Рассмотрим следующий метод проверки на равенство, взятый из раздела (Item) 10 книги Effective Java:
@Override public boolean equals(Object o) {
return (o instanceof CaseInsensitiveString) &&
((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
}
С шаблоном проверки типа его можно переписать понятнее:
@Override public boolean equals(Object o) {
return (o instanceof CaseInsensitiveString cis) &&
cis.s.equalsIgnoreCase(s);
}
Грамматика instanceof расширяется соответствующим образом:
RelationalExpression:
...
RelationalExpression instanceof ReferenceType
RelationalExpression instanceof Pattern
Pattern:
ReferenceType Identifier
Дальнейшая работа
Будущие JEP расширят язык программирования Java возможностью Pattern Matching для других конструкций языка, таких как выражения и операторы switch.
Альтернативы
Преимущества шаблонов проверки типа можно было бы получить за счёт flow typing (уточнение типа по потоку управления) в операторах if или с помощью конструкции type switch (switch по типу). Pattern Matching обобщает обе эти конструкции.
Зависимости
В реализации может использоваться JEP 309 (Dynamic Class-File Constants).