Dopasowanie wzorców - początek
Java 14 zawiera wiele nowości. Jedną z nich (jako preview feature) jest dopasowywanie wzorców z instanceof
(ang. pattern matching with instanceof
). Ludzie programujący w językach wspierających paradygmat funkcyjny, np. w Scali, w Kotlinie (o Haskellu nie wspominając), w pierwszym odruchu widzą oczyma duszy swojej od razu piękne wyrażenie match
/ when
, wyłuskiwanie danych, strażniki, dekonstruktory w obiektach stowarzyszonych itd.
Apetyt mamy wielki. Od czasu Javy 9 i wydań co 6 miesięcy raczej przeszliśmy na metodę małych kroczków (lub, wedle uznania, powolnego gotowania żaby). I pierwszym takim małym kroczkiem, który jawnie zawiera nazwę “dopasowanie wzorców” jest JEP 305: Pattern Matching for instanceof (Preview). (Inna sprawa czy wyrażenia switch nie są wcześniejszą próbą rozpalenia ognia pod tą żabą, ale w nazwie JEPy nic o “dopasowywaniu” nie mają.)
Dopasowanie po typie
W mojej ocenie ten JEP poza bycia miłym dobrego początkiem pozwala na eliminację zbędnego i “oczywistego kodu”. Rzućmy okiem™ na poniższy kod, w którym sprawdzamy, czy object
to Integer
i jeśli tak, to podwajamy go:
1if (object instanceof Integer) {
2 Integer integer = (Integer) object;
3 int doubled = integer * 2;
4 //tutaj coś dalej robimy z doubled...
5}
Linia 2. z powyższego przykładu jest dla wielu osób dość kontrowersyjna oraz (bądźmy szczerzy) stanowi powód, by twierdzić, że “Java jest przegadana”. Coś może być na rzeczy, bo skoro już w pierwszej linii stwierdziliśmy, że to musi być Integer
, to czy to rzutowanie rzeczywiście jest takie konieczne? Nie można wykorzystać object
od razu w linii 2. jak Integer
. “No przecież wiem już, że to jest Integer
, wpuściłeś mnie w ten if
, o co jeszcze ci chodzi??!!1jeden”
Na ratunek od Javy 14 śpieszy wspomniany wcześniej JEP 305. Dzięki niemu powyższy kod możemy teraz zapisać tak:
1 if (object instanceof Integer integer) {
2
3 int doubled = integer * 2;
4 //tutaj coś dalej robimy z doubled...
5 }
6
Co się stało się z drugą linią? Ano rzutowanie nie jest teraz już potrzebne. Teraz zamiast rzutowania wystarczy zapis instanceof <Klasa>
dokończyć nazwą, która w bloku będzie nazwą zmiennej dla obiektu, jeśli przejdzie on dopasowanie do <Klasy>
. W naszym przypadku object
został z powodzeniem dopasowany do Integer
, dlatego w bloku będzie widoczny jako Integer integer
. Taki jakby “alias po rzutowaniu”. I rzutowanie nie jest już potrzebne.
Podbijamy stawkę
To nie koniec atrakcji. Do akcji wkracza bowiem wykorzystanie dopasowanego obiektu już w warunku if
.
Załóżmy taką “potrzebę biznesową”, że podwajamy tylko liczby ujemne. Po staremu trzeba to napisać tak:
1if (object instanceof Integer) {
2 Integer integer = (Integer) object;
3 if (integer < 0) {
4 int doubled = integer * 2;
5 //tutaj coś dalej robimy z doubled...
6 }
7}
W takim wypadku, poza znanym nam już rzutowaniem z linii 2., trzeba jeszcze zagnieździć kolejne sprawdzenie. Zaczyna się robić paskudnie, prawda? Można ten kod “czyścić”, można wydzielać metodę do podwajania itd. Albo można wykorzystać JEP 305 jeszcze bardziej i zapisać to tak:
1if (object instanceof Integer integer && integer < 0) {
2
3
4 int doubled = integer * 2;
5 //tutaj coś dalej robimy z doubled...
6
7}
Tym razem zniknęły dwie/trzy linie (zależy jak liczyć i formatować kod), ale przede wszystkim zniknęło nam zagnieżdżenie kolejnego bloku! (Celowo zostawiłem puste linie dla lepszej czytelności przykładu i unaocznienia absencji tychże linii. Normalnie nikt tak nie będzie pisał…)
Parafrazując: chcemy podwajać nasz obiekt, jeśli pasuje do wzorca: “to jest liczba całkowita i do tego ujemna”. Stąd ten JEP nazywa się “dopasowywanie do wzorca z instanceof”.
W kolejnych wersjach Javy czekają nas bardziej wysublimowane formy dopasowań, z wyłuskiwaniem i innymi atrakcjami. Do tego czasu zachęcam do zabawy tym, co już jest. Na przykład tym przykładem.
O tym, jak moim zdaniem nie wykorzystywać dopasowania wzorców z instanceof, napisałem w kolejnym wpisie.