by (delegare) în Kotlin

by trimite implementarea unei interfețe către alt obiect (delegare de clasă) sau citirea unei proprietăți către un delegat.

Sintaxă
class Nume(delegat: Interfata) : Interfata by delegat {
    override fun oMetoda() = ...    // restul metodelor merg la delegat
}

val x by lazy { ... }              // delegarea unei proprietăți

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): eroare delegation 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() și hashCode() să fie delegate: ele vin din Any, nu din interfață, deci println(cumparaturi) din exemplul 1 ar afișa ceva de forma ListaNumarata@74a14482. Suprascrie-le dacă ai nevoie de ele.
  • Confuzia dintre class A : I by b (delegare de clasă) și val 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 { … }.