In Kotlin, la delegazione del comportamento è implementata attraverso il meccanismo linguistico della parola chiave by, direttamente nella firma della classe. Questo consente di trasferire automaticamente le chiamate ai metodi dell'interfaccia (o di più interfacce) a un altro oggetto con implementazione, riducendo il boilerplate e facilitando la composizione.
L'emergere della delegazione dell'interfaccia è un tentativo di eliminare le limitazioni e gli svantaggi dell'ereditarietà multipla. È l'idea del "composition over inheritance" - delegiamo il comportamento senza ricorrere all'ereditarietà della classe. Ispirato da linguaggi dove la composizione è più popolare (ad esempio, Go, Scala).
In Java e in altri linguaggi, spesso è necessario creare un'interfaccia e implementare manualmente ogni metodo, trasferendo la logica a un altro campo (Object Adapter pattern), che diventa rapidamente obsoleto con l'aumento del numero di metodi.
Kotlin consente di delegare dichiarativamente un'interfaccia utilizzando by:
interface Logger { fun log(msg: String) } class ConsoleLogger: Logger { override fun log(msg: String) = println(msg) } class Service(logger: Logger): Logger by logger { fun doWork() { log("Work started") // ... } } val service = Service(ConsoleLogger()) service.doWork()
Caratteristiche chiave:
Cosa succede se nella classe Service si aggiunge il proprio metodo dell'interfaccia con la stessa firma?
La propria implementazione "sovrascrive" la delegata - quindi prevale il metodo definito esplicitamente nella classe:
class Service(logger: Logger): Logger by logger { override fun log(msg: String) = println("PREFIX: $msg") }
Un classe può delegare più interfacce a oggetti diversi?
Sì, una classe può implementare e delegare più interfacce a oggetti diversi, ma ogni interfaccia è delegata a un unico oggetto:
class Service( logger: Logger, tracker: Tracker ): Logger by logger, Tracker by tracker
In cosa si differenzia la delegazione di un'interfaccia dalla delegazione delle proprietà tramite by?
La classe implementa manualmente l'interfaccia, ogni metodo chiama il delegato, e all'aggiunta di nuovi metodi dimenticano di aggiornare la proxy, il che porta a errori.
Vantaggi:
Svantaggi:
Si utilizza la delegazione linguistica, solo i metodi non standard vengono implementati all'interno della classe, nuova funzionalità viene aggiunta senza grandi cambiamenti.
Vantaggi:
Svantaggi: