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.