W Kotlinie delegowanie zachowania jest zrealizowane za pomocą językowego mechanizmu słowa kluczowego by, bezpośrednio w sygnaturze klasy. Pozwala to na automatyczne przekazywanie wywołań metod interfejsu (lub kilku interfejsów) do innego obiektu z implementacją, zmniejszając boilerplate i ułatwiając kompozycję.
Pojawienie się delegowania interfejsu to próba usunięcia ograniczeń i wad wielokrotnego dziedziczenia. To idea "kompozycja zamiast dziedziczenia" – delegujemy zachowanie bez korzystania z hierarchii klas. Zapożyczone z języków, gdzie kompozycja jest bardziej popularna (np. Go, Scala).
W Javie i innych językach często trzeba tworzyć interfejs i ręcznie implementować każdą metodę, przekazując logikę do innego pola (wzorzec Object Adapter), co szybko staje się nieaktualne w miarę wzrostu liczby metod.
Kotlin umożliwia deklaratywne delegowanie interfejsu za pomocą 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("Praca rozpoczęta") // ... } } val service = Service(ConsoleLogger()) service.doWork()
Główne cechy:
Co się stanie, jeśli w klasie Service dodamy swoją własną metodę interfejsu o tej samej sygnaturze?
Własna implementacja "przykrywa" delegowaną — czyli przeważa metoda zdefiniowana jawnie w klasie:
class Service(logger: Logger): Logger by logger { override fun log(msg: String) = println("PREFIX: $msg") }
Czy jedna klasa może delegować kilka interfejsów do różnych obiektów?
Tak, klasa może zaimplementować i delegować kilka interfejsów do różnych obiektów, ale każdy interfejs jest delegowany do jednego obiektu:
class Service( logger: Logger, tracker: Tracker ): Logger by logger, Tracker by tracker
Czym delegowanie interfejsu różni się od delegowania właściwości przez by?
Klasa ręcznie implementuje interfejs, każda metoda wywołuje delegata, przy dodawaniu nowych metod zapomniano zaktualizować proxy, co prowadzi do błędów.
Plusy:
Minusy:
Używane jest delegowanie językowe, tylko niestandardowe metody są implementowane wewnątrz klasy, nowa funkcjonalność dodawana jest bez większych zmian.
Plusy:
Minusy: