Ce face null și null safety (?. ?: !!)
null înseamnă că o variabilă nu are nicio valoare. În Kotlin, un tip obișnuit (String, Int) nu poate primi niciodată null; compilatorul nu te lasă. Ca să accepte null, tipul are un semn de întrebare: String?. Pe o valoare String? nu poți apela direct .length: întâi trebuie să tratezi cazul null. Așa dispar cele mai multe erori NullPointerException din Java.
Operatorii pentru null: ?. (apel sigur) apelează metoda doar dacă valoarea nu e null, altfel dă null; ?: (elvis) dă o valoare de rezervă când stânga e null; !! spune „sunt sigur că nu e null” și aruncă NullPointerException dacă te-ai înșelat. După un if (x != null), compilatorul tratează x ca nenul (smart cast).
Multe funcții din biblioteca standard întorc tipuri ? exact ca să te oblige să tratezi lipsa valorii: toIntOrNull(), firstOrNull(), map[cheie], readlnOrNull(). Combinația x?.let { ... } execută un bloc doar când valoarea există.
Exemple
Exemplul 1: ?. și ?: pe valori care pot lipsi
map[cheie] întoarce String?, pentru că orașul poate lipsi. ?. apelează uppercase doar dacă există, iar ?: dă textul de rezervă.
fun main() {
val orase: Map<String, String> = mapOf("Ana" to "Iași", "Mihai" to "Brașov")
for (nume in listOf("Ana", "Ioana")) {
val oras: String? = orase[nume]
println("$nume: ${oras?.uppercase() ?: "oraș necunoscut"}")
println(" lungime: ${oras?.length}")
}
}
Rezultat
Ana: IAȘI
lungime: 4
Ioana: oraș necunoscut
lungime: null
Exemplul 2: if (x != null), let și filterNotNull
După verificarea cu if, nota este folosită ca Int. let rulează doar pentru valorile nenule.
fun main() {
val raspunsuri = listOf("9", "zece", "7", "")
for (text in raspunsuri) {
val nota: Int? = text.toIntOrNull()
if (nota != null) {
println("'$text' -> nota $nota, dublată ${nota * 2}")
} else {
println("'$text' nu este o notă")
}
}
raspunsuri.firstOrNull { it.startsWith("z") }?.let { println("Primul text cu z: $it") }
val valide: List<Int> = raspunsuri.map { it.toIntOrNull() }.filterNotNull()
println("Media notelor valide: ${valide.average()}")
}
Rezultat
'9' -> nota 9, dublată 18
'zece' nu este o notă
'7' -> nota 7, dublată 14
'' nu este o notă
Primul text cu z: zece
Media notelor valide: 8.0
Exemplul 3: !! și NullPointerException
!! nu verifică nimic: când valoarea este null, aruncă NullPointerException. Aici excepția este prinsă ca să vezi ce se întâmplă.
fun gasesteTelefon(nume: String): String? =
mapOf("Ana" to "0722 111 222")[nume]
fun main() {
println("Ana: ${gasesteTelefon("Ana")!!}")
try {
println("Dan: ${gasesteTelefon("Dan")!!}")
} catch (e: NullPointerException) {
println("Eroare: ${e::class.simpleName}")
}
println("Dan, în siguranță: ${gasesteTelefon("Dan") ?: "fără telefon"}")
}
Rezultat
Ana: 0722 111 222
Eroare: NullPointerException
Dan, în siguranță: fără telefon
Greșeli frecvente
- Folosirea lui
!!peste tot ca să „scapi” de erorile de compilare: aduce înapoiNullPointerException. Preferă?.,?:sau unif. ?.sau?:pe un tip care nu poate fi null: compilatorul avertizează (unnecessary safe call), iar cu-Werrorcompilarea eșuează.- Lanțul
a?.b.c: dacăae null,a?.be null și apoi.cnu compilează. Scriea?.b?.c.
Comenzi înrudite
is, !is și smart cast: is verifică tipul unei valori; după verificare, compilatorul o tratează automat ca acel tip (smart cast).as și as? (conversie de tip): as tratează o valoare ca alt tip (aruncă excepție dacă nu se potrivește); as? întoarce null în loc de excepție.lateinit: lateinit permite unei proprietăți var ne-nullable să primească valoare mai târziu, după crearea obiectului.return: Încheie funcția curentă și întoarce o valoare apelantului; cu etichetă (return@forEach) iese doar din lambda.true și false (Boolean): true și false sunt cele două valori ale tipului Boolean, folosite în condiții, comparații și operatori logici.