Ce face Proprietăți delegate (by lazy, observable, map)
Cu by, citirea și scrierea unei proprietăți sunt trimise unui obiect delegat. Biblioteca standard are delegați gata făcuți: lazy { } calculează valoarea la prima citire și o păstrează; Delegates.observable(initial) { ... } rulează codul tău după fiecare schimbare; Delegates.vetoable poate refuza o schimbare; un Map poate fi delegat, iar proprietatea citește valoarea de la cheia cu același nume.
Poți scrie și propriul delegat: o clasă cu operator fun getValue(thisRef, property) (și setValue pentru var). property.name îți dă numele proprietății. Delegații sunt utili când același comportament (validare, jurnalizare, salvare) se repetă la multe proprietăți.
by lazy este sigur pentru mai multe fire în mod implicit: valoarea se calculează o singură dată chiar dacă mai multe fire o citesc deodată.
Exemple
Exemplul 1: by lazy: calcul la prima citire
Mesajul „calculez” apare o singură dată, la prima citire, nu la crearea obiectului.
class Raport(private val vanzari: List<Int>) {
val total: Int by lazy {
println("(calculez totalul...)")
vanzari.sum()
}
}
fun main() {
val r = Raport(listOf(120, 340, 95))
println("Raport creat")
println("Total: ${r.total} lei")
println("Din nou: ${r.total} lei")
}
Rezultat
Raport creat
(calculez totalul...)
Total: 555 lei
Din nou: 555 lei
Exemplul 2: observable și vetoable
observable afișează fiecare schimbare. vetoable returnează false pentru o valoare negativă, deci schimbarea nu are loc.
import kotlin.properties.Delegates
class Comanda {
var stare: String by Delegates.observable("plasată") { _, vechi, nou ->
println("Starea: $vechi -> $nou")
}
var cantitate: Int by Delegates.vetoable(1) { _, _, nou -> nou > 0 }
}
fun main() {
val c = Comanda()
c.stare = "expediată"
c.stare = "livrată"
c.cantitate = 5
c.cantitate = -2
println("Cantitate: ${c.cantitate}")
}
Rezultat
Starea: plasată -> expediată
Starea: expediată -> livrată
Cantitate: 5
Exemplul 3: Delegare către Map și delegat propriu
nume și oras se citesc din map după numele lor. Majuscule este un delegat propriu care transformă orice text primit.
import kotlin.reflect.KProperty
class Majuscule {
private var valoare = ""
operator fun getValue(thisRef: Any?, property: KProperty<*>) = valoare
operator fun setValue(thisRef: Any?, property: KProperty<*>, nou: String) {
println("Setez ${property.name} = $nou")
valoare = nou.uppercase()
}
}
class Client(date: Map<String, Any?>) {
val nume: String by date
val oras: String by date
var cod: String by Majuscule()
}
fun main() {
val c = Client(mapOf("nume" to "Elena Ionescu", "oras" to "Sibiu"))
c.cod = "ro-sb-17"
println("${c.nume} din ${c.oras}, cod ${c.cod}")
}
Rezultat
Setez cod = ro-sb-17
Elena Ionescu din Sibiu, cod RO-SB-17
Greșeli frecvente
var x by lazy { }:lazymerge doar cuval.- Delegare către un
Mapfără cheia respectivă: citirea aruncăNoSuchElementException(Key oras is missing in the map). - Lipsa lui
import kotlin.properties.Delegates:Delegatesnu este importat automat.
Comenzi înrudite
lazy: Creează o valoare calculată abia la prima folosire, apoi păstrată; se folosește cu val nume by lazy { … }.by (delegare): by trimite implementarea unei interfețe către alt obiect (delegare de clasă) sau citirea unei proprietăți către un delegat.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.lateinit: lateinit permite unei proprietăți var ne-nullable să primească valoare mai târziu, după crearea obiectului.operator: operator marchează o funcție cu nume special (plus, get, contains, compareTo…) ca să poată fi folosită prin +, [], in sau <.