Pattern matching: синтаксис



В прошлый раз мы обсудили pattern matching в вакууме. Сегодня обсудим, как он выглядит в java с учётом 21 версии, и чего не хватает в текущей реализации.



Под зонтик pattern matching относят много фич: sealed классы, records, обновление switch и instanceOf. Sealed и records в контексте pattern matching — специфичные кейсы data oriented programming, не будем о них. Cфокусируемся на более популярных сценариях:)



Итак, как pattern matching воплощается в синтаксисе:



1️⃣ Компактный instanceOf



Раньше для неизвестного типа выполнялись две операции: instanceOf и явное приведение типа:



if (obj instanceof String) {

String str = (String) obj;

// используем str

}



Теперь эти операции объединены. Рядом с instanceOf объявляем имя переменной и сразу ей пользуемся:



if (obj instanceof String str) {

// используем str

}



Тонкий момент— переменная определяется только при успешном выполнении instanceOf, поэтому есть нюанс с областью видимости.



В логическом И можно использовать переменную в том же if:

if (obj instanceof String s && s.length() > 5)



Логическое ИЛИ такого не позволяет, будет ошибка компиляции:

if (obj instanceof String s || s.length() > 5)



2️⃣ Компактный switch



У древнейшей конструкции доступен новый синтаксис:

▫️ Стрелочка вместо двоеточия

▫️ Break в конце case по умолчанию, и его можно не писать

▫️ Можно объединить несколько case в один



Старый синтаксис никуда не делся, им можно пользоваться:



switch (value) {    

case 1:

case 3: println("Odd"); break;

case 2:

case 4: println("Even"); break;

default: println("Other");

}



В "новом стиле" это будет так:



switch (value) {    

case 1,3 -> println("Odd");

case 2,4 -> println("Even");

default -> println("Other")

}



Важный момент — break добавляется по умолчанию только в вариант "со стрелочками". В "двоеточиях" break всё ещё добавляется вручную.



🔥 Ответ на вопрос перед постом: выведется Even Other Even. Чтобы работало как надо, нужно переписать на стрелочки или добавить break.



3️⃣ Присвоение элемента через switch



int value = switch(…) {…};



4️⃣ Проверка в switch по типам и сложные case



Раньше в case принимались только константы. Теперь можно проверить тип переменной:



switch (obj) {

case Integer i -> …

case Long l -> …

case Double d -> …

}



Если произошёл мэтч, для переменной сразу доступна доп. информация, и можно добавить условия в case:



switch (response) {

case String s when s.length() == 10 -> …

}



А теперь самое интересное! Обсудим, чего в текущей реализации нет:



Нет работы с массивами



Один из кейсов pattern matching — работа с набором данных, в которых мы ищем определённые признаки. Такого в java пока нет:



double discount = switch(transaction) {

case ["vip", Long, _, _, Double sum] → sum*0,9

case _ → 0

}



В других языках такой функционал есть. Например, List patterns в С# или Matching sequences в питоне.



Нет паттернов по полям класса



Под капот спрятан только instanceOf, остальные условия выглядят как в обычном if. Вся работа с полями происходит через методы:



switch (figure) {

case Square s when s.getLength() != 10 → …

}



В том же питоне запись гораздо компактнее:



match point:

case (0, 0): …

case (0, y): …

case _: …



JEP Record Patterns мог быть как раз об этом, ведь records позиционируются как лаконичные контейнеры данных. Но увы.



Резюме



В текущем виде pattern matching выглядит слабо, особенно по сравнению с другими языками. Покрыты только базовые кейсы, в таком виде область применения очень ограничена.



Возможно это лишь промежуточный этап. Посмотрим, как фича будет развиваться и использоваться