Historia pytania:
Operator Elvis (?:) pojawił się w Kotlinie jako zwięzły i bezpieczny sposób obsługi wartości nullable. Nazwa pochodzi od podobieństwa do fryzury Elvisa Presleya, gdy spojrzeć na operator z boku. Celem jest pozbycie się szablonowego kodu w stylu if (a != null) a else b i uczynienie codziennego programowania bardziej wygodnym.
Problem:
Bezpośredni dostęp do wartości, która może być null, prowadzi do błędu w czasie wykonania (NullPointerException). Dlatego potrzebne jest narzędzie, które elegancko podstawia wartości domyślne, gdy zmienna okazuje się null.
Rozwiązanie:
Operator Elvis jest stosowany do typów nullable. Jeśli wyrażenie po lewej stronie operatora nie jest równe null, to ono jest zwracane, w przeciwnym razie zwracane jest wyrażenie po prawej stronie. To sprawia, że kod staje się bardziej kompaktowy i bezpieczny.
Przykład kodu:
fun getLength(str: String?): Int { return str?.length ?: 0 } val result = getLength(null) // result == 0 val result2 = getLength("Hello") // result2 == 5
Kluczowe cechy:
Co się stanie, jeśli po prawej stronie operatora Elvis będzie wyrażenie z efektem ubocznym? Czy będzie ono zawsze wywoływane?
Nie! Wyrażenie po prawej stronie jest obliczane tylko wtedy, gdy po lewej stronie znajduje się null. To może mieć istotne znaczenie, jeśli funkcja ma efekty uboczne lub kosztowne obliczenia.
Przykład kodu:
var called = false val x: String? = "test" val y = x ?: run { called = true; "default" } // called będzie równe false, ponieważ run nawet się nie wykona
Czy można użyć operatora Elvis do wyrzucania wyjątków?
Tak, w Kotlinie można pisać tak:
fun getLengthStrict(str: String?): Int = str?.length ?: throw IllegalArgumentException("str jest null")
Jeśli str jest równy null, zostanie wyrzucony wyjątek. To wygodny mechanizm walidacji danych.
Czy można "łączyć" kilka operatorów Elvis jeden po drugim?
Tak, to zwykłe podejście do fallbacków:
val name = fromDatabase ?: fromCache ?: "Unknown"
To wyrażenie zwróci pierwszą WARTOŚĆ NIE null lub ciąg "Unknown".
Wielopoziomowe zagnieżdżenie sprawdzania null przez operatora Elvis:
val title = a?.b?.c?.d?.e ?: defaultTitle
Plusy:
Minusy:
Jasna dekompozycja krok po kroku, zrozumiałe nazwy zmiennych:
val deepValue = a?.b val deeperValue = deepValue?.c?.d ?: default
Plusy:
Minusy: