openjdk.ruOpenJDK на русском

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).