Pattern matching w Kotlinie

Dziś znowu ketchup, ale tym razem coś, czego w Javie nie ma. Jest w scali, jest w językach funkcyjnych, ale w javie nie ma.

Co to pattern matching?

W najprostszych słowach jest to konstrukcja języka, która pozwala na budowanie czegoś w rodzaju rozbudowanych instrukcji warunkowych, ale z decydowanie prostszy i przyjemniejszy dla oka sposób. Zazwyczaj wyrażenie takie dopuszcza też pewne skrótowe zapisy, które w postaci wyrażenia warunkowego były, by trudne do zrozumienia.

W Kotlinie dopasowanie wzorców (bo tak to się po polsku nazywa) odbywa się z użyciem słowa kluczowego when. Najprostszym zastosowaniem jest użycie zamiast ifa:

Listing 1. Najprostsze użycie dopasowań

fun main(args: Array<String>) {

    val person = Person("Jan", "Kolwaski", "kowalski@jan.pl", 32L, null);

    when(person.firstName) {
        "Jan" -> print("ok")
        else -> print("nok")

    }
}

Trochę przykładów

Oczywiście nie tylko proste dopasowania jak to powyżej jest możliwe. Spójrzmy na dopasowanie za pomocą sprawdzenia typu:

Listing 2. Dopasowanie typu

open class Sex;
class Man : Sex();
class Woman : Sex();

fun main(args: Array<String>) {

    val s:Sex;

    s = Man();

    when (s) {
        is Man -> print("Sir!")
        is Woman -> print("Lady!")
        is Sex -> print("No cześć!")
    }
}

Zamiana kolejności warunków spowoduje inne zachowanie programu. Generalna zasada jest taka, że dla danej wartości jest wykonywany kod pierwszego pasującego dopasowania.

Listing 3. Zamiana kolejności wzorców

open class Sex;
class Man : Sex();
class Woman : Sex();

fun main(args: Array<String>) {

    val s:Sex;

    s = Man();

    when (s) {
        is Sex -> print("No cześć!")
        is Man -> print("Sir!")
        is Woman -> print("Lady!")
    }
}

## No cześć!

W przypadku gdy żaden wzorzec nie zostanie dopasowany, to nic się nie stanie:

Listing 4. Brak dopasowania

open class Sex;
class Man : Sex();
class Woman : Sex();

fun main(args: Array<String>) {

    val s:Sex;

    s = Man();

    when (s) {
        is Woman -> print("Lady!")
    }
}

Słowo kluczowe else z listingu 1 podpowiada nam, że jeżeli nie znajdziemy dopasowania, to zostanie wykonany kod związany z tym, specjalnym, dopasowaniem. Możemy też popracować z bardziej zawiłymi warunkami:

Listing 5. Dopasowania z zakresami i metodami

fun main(args: Array<String>) {

    val i: Int = 1

    when (i) {
        0, 1 -> print("Binary!")
        in 0..10 -> print("Decimal!")
        parseInt(args[0]) -> print("Eq param")
        else -> print("WUT?")
    }
}

W tym kodzie mamy dopasowania w zakresie za pomocą in, które możemy oczywiście negować za pomocą !in. Obecne jest też dopasowanie z metodą, ale zawiera ono małą pułapkę. Jeżeli będziemy chcieli dopasować trzeci wzorzec, to całość wysypie się z ArrayIndexOutOfBoundsException. Nie za bardzo jest jak to obejść, ponieważ musimy zachować zgodność typów pomiędzy parametrem, a wzorcem. Jedyną metodą jest delegacja tego dopasowania do osobnej metody, która przyjmie dwa parametry i zwróci Int tak, by można było wzorzec zastosować. Jednak takie rozwiązanie ma dużo wewnętrznego bleh.

Podsumowanie

Dopasowanie wzorców w kotlinie działa w sposób zbliżony do instrukcji switch w javie. Instrukcja when jest jednak zacznie bardziej elastyczny i pozwala na znacznie więcej.

4 myśli na temat “Pattern matching w Kotlinie

  1. Tak, znam i używam czasami. Generalnie jest tak kilka rzeczy zrobionych lepiej niż standardowym API i jest kilka rzeczy ekstra.

Napisz odpowiedź

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax