Gdy kilka dni temu wrzuciłem wpis o tuplach, prawie od razu pojawiły się pytania:

hmmm…. a do czego takie dziwne konstrukcje ? taka sztuka dla sztuki

Samodzielnie krotki rzeczywiście są mało przydatne. Co prawda czasami warto ich używać np. gdy potrzebujemy POJO, a nie można go stworzyć albo chcemy sobie zapewnić kolejność typów w strukturze. Jednak znacznie więcej możliwości dają nam krotki w połączeniu z dopasowaniem wzorców. Dziś zaczniemy omawiać ten mechanizm i jego implementację w Javaslang. Nie jest on rozwiązaniem natywnym, jak na przykład w Kotlinie, ale i tak daje on dużo możliwości.

Dopasowanie dla wartości

To co ułatwi nam życie jest statyczny import całego javaslang.API..

Jeżeli chcemy dopasować jakąś wartość do wzorca by uzyskać np. odpowiedni obiekt możemy użyć ifologii. Proste, łatwe i zrozumiałe, prawda?

Listing 1. Ifologia w praktyce

public void ifology() {
    Integer i = 0;
    String on;
    if (i == 1) {
        on = "Jeden";
    } else if (i == 2) {
        on = "Dwa";
    } else {
        on = "?";
    }
    System.out.println(on);
}

Raptem trzy proste warunki i bardzo dużo kodu. Co będzie w przypadku gdy przetwarzamy plik binarny, blok po bloku i chcemy odpowiednio interpretować nagłówki bloków? No delikatnie mówiąc przejebane. Zamiast tego możemy napisać tak:

Listing 2. Pattern matching dla wartości

public void introduction() {
    Integer i = 0;
    String on = Match(i).of(
            Case($(1), "Jeden"),
            Case($(2), "Dwa"),
            Case($(), "?")
    );
    System.out.println(on);
}

Przy dużej liczbie warunków kod nadal będzie długi, ale znacznie bardziej czytelny. Pary dopasowanie – wartość pozwalają na lepsze zrozumienie kodu.

// offtopic

Zapewne wiele osób powie, że nie warto kombinować, bo ify są łatwiejsze w zrozumieniu. Nie do końca. Problemem jest nieznajomość API, idiomów i konstrukcji z biblioteki. Jeżeli je poznamy, to okazuje się, iż czytanie kodu napisanego tak jak na listingu 2 jest naturalne. Równie dobrze można zarzucić komuś, że pisze niezrozumiały kod w Javie, bo w C# pisze się zupełnie inaczej. Szczytem tego typu dyskusji są te o notacji.

// koniec offtopicu

Dopasowanie tworzymy za pomocą metody $(wartość). Dopasowania są sprawdzane po kolei i pierwsze pasujące zwraca wynik. Metoda $() jest dopasowaniem w rodzaju „dla każdego”. Jeżeli jej nie umieścimy, to otrzymamy błąd MatchError:

Listing 3. Brak dopasowania domyślnego prowadzi do błędu

public void introduction2() {
    Integer i = 0;
    String on = Match(i).of(
            Case($(1), "Jeden"),
            Case($(2), "Dwa")
    );
    System.out.println(on); // nie wyświetli się
}

Jeżeli wiemy, że może dojść do takiej sytuacji, a nie mamy reguł dla domyślnego dopasowania, to możemy z korzystać z opcji:

Listing 4. Dopasowanie i Option

public void introduction3() {
    Integer i = 0;
    Option<String> on = Match(i).option(
            Case($(1), "Jeden"),
            Case($(2), "Dwa")
    );
    System.out.println(on);
}

Wynikiem będzie None. Javaslang używa własnych kontenerów wartości. Warto o tym pamiętać.

Podsumowanie

Mając dopasowania dla wartości, możemy już znacznie ulepszyć nasz kod. Łącząc je z leniwą ewaluacją wartości, można uzyskać całkiem ciekawe rozwiązania niektórych problemów.