Pattern matching: зачем?



Последние годы в джаву активно добавляется группа фич на тему pattern matching.



Тагир Валеев из Intellij IDEA в этом докладе рассказывает, какие это сложные фичи, как много челленджей стояло перед командой разработки. Но не говорит, зачем он вообще нужен.



Архитектор java Brian Goetz пишет, что pattern matching — не просто синтаксический сахар, а движение в сторону data oriented programming. Но это слабо применимо к большинству корпоративных систем, где доминирует ООП и изредка встречается функциональное программирование.



Большая часть моих знакомых считает паттерн матчинг "стрелкой вместо двоеточия в свиче":)



Так что сегодня расскажу, для каких задач пригодится pattern matching. В следующем посте обсудим реализацию в java.



Формат входных данных в энтерпрайзе обычно чётко определён. Если входящее сообщение не подходит по формату — это ошибка со стороны клиента.



Но бывает, что система работает со множеством источников данных, и выделить общий формат очень сложно.



Например, вы парсите чужие сайты/документы/дампы и достаёте оттуда что-то полезное. Знаю один проект, где бизнес-данные вытаскивают из логов(!) другой системы🤯



В итоге входные данные выглядят как [Object, Object, Object].



Паттерн — схема того, что мы ищем. Паттерн для поиска координат может выглядеть так:



▫️ [Double, Double] — ищём массив из двух чисел с плавающей запятой

▫️ [(-90;90), (-180,180)] — массив с двумя числами в указанных диапазонах



Дальше идём по набору данных и проверяем их на соответствие паттерну.



Традиционно для такой задачи используется связка if + instanceOf. Вариант рабочий, но читаемость ужасная.



Вот что нужно написать, чтобы проверить, является ли координатами массив [Object, Object] data:



if (data.length == 2 && data[0] instanceOf Double && data[1] instanceOf Double) {

double n = (Double) data[0];

double e = (Double) data[1];

if (n ≥ -90 && n≤ 90 && e ≥ -180 && e ≤ 180) {

// что-то делаем

}

}



В pattern matching многие проверки убираются под капот, и код выглядит симпатичнее:



switch(data) {

case [(-90; 90), (-180, 180)]:

// что-то делаем

}



Близкий родственник паттерн матчинга — регулярные выражения. Строка — это набор символов, внутри этого набора ищутся паттерны. Можно сделать ту же работу через if, но регулярка удобнее. Плюс в строке элементы однородные (символы), а паттерн матчинг работает с разными типами данных.



Ещё пример:



double discount = switch(transaction) {

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

case _ → 0

}



Здесь ищем список из 5 элементов, где первый — строка "vip", второй и пятый — число. Если нашли — можно сразу работать с полем sum. Если не нашли — используем паттерн по умолчанию _



С if-ами эта конструкция будет гораздо объёмнее



Резюме



Pattern matching нужен, когда мы пытаемся найти что-то знакомое в слабо- или неструктурированных данных.

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

Разумеется, применить pattern matching можно и в других сценариях, но здесь видится наибольший профит в корпоративном царстве ООП.



В следующем посте распишу возможности pattern matching конкретно в джаве.

Спойлер: пока не впечатляет😑