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

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 обобщает обе эти конструкции.