lateinit în Kotlin

Clase și obiecte Kotlin 1.0 (isInitialized și lateinit local: Kotlin 1.2)

lateinit permite unei proprietăți var ne-nullable să primească valoare mai târziu, după crearea obiectului.

Sintaxă
lateinit var nume: Tip        // Tip ne-nullable, nu primitiv

if (this::nume.isInitialized) { ... }

Ce face lateinit

O proprietate ne-nullable trebuie să primească valoare la creare. Uneori valoarea vine mai târziu: dintr-o metodă de configurare, dintr-un framework (Android, teste) sau după citirea unui fișier. În loc s-o faci nullable și să scrii !! peste tot, o marchezi lateinit var: compilatorul te lasă s-o declari fără valoare.

Dacă citești proprietatea înainte să-i dai valoare, programul aruncă UninitializedPropertyAccessException, cu numele proprietății în mesaj. Poți verifica înainte cu this::nume.isInitialized.

Restricții: doar var, tipul nu poate fi nullable și nu poate fi primitiv (Int, Double, Boolean). Pentru valori calculate o singură dată, la prima folosire, alege by lazy.

Exemple

Exemplul 1: Valoare primită după creare

conexiune primește valoare în porneste(). După aceea se folosește ca o proprietate obișnuită, fără ?. sau !!.

class Serviciu {
    lateinit var conexiune: String

    fun porneste(server: String) {
        conexiune = "conectat la $server"
    }

    fun trimite(mesaj: String) = println("[$conexiune] $mesaj")
}

fun main() {
    val s = Serviciu()
    s.porneste("db.exemplu.ro")
    s.trimite("Salut")
    s.trimite("Comanda 15 a fost plătită")
}

Rezultat

[conectat la db.exemplu.ro] Salut
[conectat la db.exemplu.ro] Comanda 15 a fost plătită

Exemplul 2: Citire înainte de inițializare

Mesajul excepției spune exact care proprietate nu a primit valoare.

class Raport {
    lateinit var titlu: String
}

fun main() {
    val r = Raport()
    try {
        println(r.titlu)
    } catch (e: UninitializedPropertyAccessException) {
        println("Eroare: ${e.message}")
    }
    r.titlu = "Vânzări octombrie"
    println(r.titlu)
}

Rezultat

Eroare: lateinit property titlu has not been initialized
Vânzări octombrie

Exemplul 3: Verificare cu isInitialized

this::utilizator este o referință la proprietate; isInitialized spune dacă a primit valoare.

class Sesiune {
    lateinit var utilizator: String

    fun stare() = if (this::utilizator.isInitialized) "autentificat ca $utilizator" else "neautentificat"
}

fun main() {
    val s = Sesiune()
    println(s.stare())
    s.utilizator = "ana.pop"
    println(s.stare())
}

Rezultat

neautentificat
autentificat ca ana.pop

Greșeli frecvente

  • lateinit var varsta: Int: nu este permis pentru tipuri primitive (eroare 'lateinit' modifier is not allowed on properties of primitive types). Folosește Int? sau o valoare inițială.
  • lateinit val: nu se poate, lateinit merge doar cu var.
  • Folosirea lui lateinit pentru a evita gândirea despre null: dacă proprietatea poate rămâne fără valoare în mod normal, tipul nullable este mai cinstit.

Comenzi înrudite

  • null și null safety (?. ?: !!): null înseamnă „nicio valoare”. Kotlin separă tipurile care acceptă null (String?) și oferă ?., ?: și !! pentru ele.
  • lazy: Creează o valoare calculată abia la prima folosire, apoi păstrată; se folosește cu val nume by lazy { … }.
  • get și set (getteri și setteri): get și set definesc ce se întâmplă când o proprietate este citită sau modificată; field este valoarea păstrată în spate.
  • val și var: val declară o variabilă care nu mai poate fi schimbată după atribuire; var declară una care poate primi altă valoare.
  • Proprietăți delegate (by lazy, observable, map): O proprietate delegată (val x by …) lasă un alt obiect să decidă ce se întâmplă la citire și scriere: lazy, observable, map.