Kotlin wspiera parametry dziedziczenia przez słowo kluczowe open, ale główna rekomendacja języka to zgadzanie się na kompozycję zamiast dziedziczenia. Pozwala to na tworzenie bardziej elastycznych, rozszerzalnych systemów, unikając kruchości hierarchii i problemów związanych z głębokim dziedziczeniem.
Kompozycja polega na tym, że włącza się obiekt odpowiedniego typu jako pole klasy i deleguje mu pracę, zamiast dziedziczyć jego implementację. Kotlin ułatwia wzorzec delegacji za pomocą słowa kluczowego by, co pozwala automatycznie delegować implementację interfejsów obiektom.
Przykład wzorca delegacji:
interface Logger { fun log(message: String) } class ConsoleLogger : Logger { override fun log(message: String) = println(message) } class Service(private val logger: Logger) : Logger by logger { fun doAction() { log("Działanie wykonane") } } fun main() { val service = Service(ConsoleLogger()) service.doAction() // Wydrukuje: Działanie wykonane }
Takie podejście upraszcza ponowne użycie kodu i sprawia, że logika jest bardziej modularna.
"Czy klasa danych może dziedziczyć od innej klasy, na przykład od klasy abstrakcyjnej?"
data class w Kotlinie nie może dziedziczyć od innej klasy (oprócz interfejsów), ponieważ data class jest zawsze finalna. Wyjątek stanowią interfejsy, które można zaimplementować.Przykład:
abstract class Base(val name: String) data class Derived(val age: Int, val name: String) : Base(name) // Błąd kompilacji: data class nie może rozszerzać klasy Base
Ale możliwe jest:
interface User data class Admin(val name: String, val rights: List<String>) : User
Historia
Na projekcie zdecydowano się dziedziczyć kilka serwisów od wspólnej klasy abstrakcyjnej, aby zrealizować powtarzającą się logikę. W rezultacie powstało wiele poziomów dziedziczenia, co skomplikowało debugowanie i spowodowało problemy z testowaniem. Po przejściu na kompozycję i delegację (przez interfejsy i wstrzykiwanie zależności) udało się uprościć kod, uczynić go bardziej modularnym i zwiększyć pokrycie testami.
Historia
Początkujący programista próbował rozszerzyć klasę danych za pomocą innej klasy, aby dodać wspólną funkcjonalność. Kod nie kompilował się, ale programista przez długi czas nie mógł zrozumieć przyczyny (ograniczenia klasy danych w Kotlinie).
Historia
W projekcie z rozbudowaną logiką logowania zdecydowano się wydzielić funkcjonalność logowania do klasy bazowej. Jednak w miarę rozwoju systemu niektóre serwisy wymagały innej implementacji logowania. Zmieniono to za pomocą interfejsu Logger i kompozycji przez delegację, co znacznie uprościło architekturę.