Ce face by (delegare)
by are două folosiri. Prima este delegarea de clasă: class ListaJurnal(l: MutableList<String>) : MutableList<String> by l. Clasa implementează interfața, iar compilatorul scrie automat toate metodele ei, trimițând fiecare apel către obiectul l. Tu suprascrii doar metodele pe care vrei să le schimbi. Este o alternativă la moștenire („compoziție în loc de moștenire”), fără zeci de metode scrise de mână.
Delegarea merge doar pentru interfețe, nu pentru clase. Metodele suprascrise de tine nu sunt văzute de delegat: dacă delegatul își apelează intern propria metodă, apelează versiunea lui, nu pe a ta.
A doua folosire este pentru proprietăți: val x by lazy { }, var y by Delegates.observable(...). Aceasta are o pagină separată.
Exemple
Exemplul 1: Listă care numără adăugările
ListaNumarata este o MutableList completă (are size, get, remove…), dar suprascrie doar add. Pentru afișare se folosește toList(), pentru că toString() nu este delegat (vezi greșelile frecvente).
class ListaNumarata<T>(private val interna: MutableList<T> = mutableListOf()) : MutableList<T> by interna {
var adaugari = 0
private set
override fun add(element: T): Boolean {
adaugari++
return interna.add(element)
}
}
fun main() {
val cumparaturi = ListaNumarata<String>()
cumparaturi.add("pâine")
cumparaturi.add("ouă")
cumparaturi.add("brânză")
cumparaturi.remove("ouă")
println("Lista: ${cumparaturi.toList()}, mărime ${cumparaturi.size}, adăugări: ${cumparaturi.adaugari}")
println("Conține pâine? ${"pâine" in cumparaturi}")
}
Rezultat
Lista: [pâine, brânză], mărime 2, adăugări: 3
Conține pâine? true
Exemplul 2: Delegare pentru a combina comportamente
Robot nu moștenește nimic: își primește componentele și le deleagă interfețele. Poți schimba ușor o componentă cu alta.
interface Vorbitor { fun vorbeste(): String }
interface Mergator { fun merge(pasi: Int): String }
class VoceRomana : Vorbitor { override fun vorbeste() = "Bună ziua!" }
class Roti : Mergator { override fun merge(pasi: Int) = "rulez ${pasi * 30} cm" }
class Robot(val nume: String, v: Vorbitor, m: Mergator) : Vorbitor by v, Mergator by m
fun main() {
val r = Robot("Robi", VoceRomana(), Roti())
println("${r.nume}: ${r.vorbeste()} Acum ${r.merge(4)}.")
}
Rezultat
Robi: Bună ziua! Acum rulez 120 cm.
Exemplul 3: Delegatul nu vede metodele suprascrise
Politicos suprascrie salut, dar prezentare rulează în delegat, care își folosește propriul salut.
interface Persoana {
fun salut(): String
fun prezentare(): String
}
class Simplu(val nume: String) : Persoana {
override fun salut() = "Salut"
override fun prezentare() = "${salut()}, sunt $nume"
}
class Politicos(p: Persoana) : Persoana by p {
override fun salut() = "Bună ziua"
}
fun main() {
val p = Politicos(Simplu("Ioana"))
println(p.salut())
println(p.prezentare())
}
Rezultat
Bună ziua
Salut, sunt Ioana
Greșeli frecvente
- Delegarea unei clase în loc de interfață (
: ArrayList<T> by l): eroaredelegation is supported only for interfaces. - Așteptarea ca delegatul să folosească metodele suprascrise (vezi exemplul 3): delegarea nu este moștenire.
- Așteptarea ca
toString(),equals()șihashCode()să fie delegate: ele vin dinAny, nu din interfață, deciprintln(cumparaturi)din exemplul 1 ar afișa ceva de formaListaNumarata@74a14482. Suprascrie-le dacă ai nevoie de ele. - Confuzia dintre
class A : I by b(delegare de clasă) șival x by lazy(delegare de proprietate): același cuvânt, mecanisme diferite.
Comenzi înrudite
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.interface: interface descrie ce poate face un obiect (metode și proprietăți), fără stare; o clasă poate implementa mai multe interfețe.override și super: override înlocuiește o metodă sau o proprietate moștenită; super apelează versiunea din clasa de bază sau din interfață.lazy: Creează o valoare calculată abia la prima folosire, apoi păstrată; se folosește cu val nume by lazy { … }.