JEP 375: Pattern Matching for instanceof (Second Preview)
Pattern Matching (сопоставление с образцом) для instanceof, вторая версия Preview (предварительная версия)
| Автор | Brian Goetz |
| Ответственный | Jan Lahoda |
| Тип | Feature |
| Область | SE |
| Статус | Closed / Delivered |
| Выпуск | 15 |
| Компонент | specification / language |
| Обсуждение | amber dash dev at openjdk dot java dot net |
| Трудоёмкость | M |
| Длительность | M |
| Связан с | JEP 305: Pattern Matching for instanceof (Preview) |
| JEP 394: Pattern Matching for instanceof | |
| Рецензенты | Alex Buckley |
| Одобрен | Mark Reinhold |
| Создан | 2019/12/02 15:00 |
| Обновлён | 2021/08/28 00:19 |
| Задача | 8235186 |
Аннотация
Добавить в язык программирования Java Pattern Matching для оператора instanceof. Pattern Matching позволяет выражать распространённую в программах логику, а именно условное извлечение компонентов из объектов, лаконичнее и безопаснее. Это Preview-возможность языка в JDK 15.
История
Pattern Matching для instanceof был предложен в JEP 305 в середине 2017 года и в конце 2019 года включён в JDK 14 как Preview-возможность языка. Этот JEP предлагает повторно выпустить эту возможность в статусе Preview в JDK 15 без изменений по сравнению с 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. Кроме того, образцы проверки типа особенно полезны при написании методов проверки равенства. Рассмотрим следующий метод проверки равенства из статьи 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. Pattern Matching обобщает обе эти конструкции.