Kotlin unterstützt Vererbung über das Schlüsselwort open, aber die Hauptempfehlung der Sprache ist, sich für Komposition anstelle von Vererbung zu entscheiden. Dies ermöglicht die Erstellung flexiblerer, erweiterbarer Systeme, wodurch Verletzlichkeit von Hierarchien und Probleme im Zusammenhang mit tiefer Vererbung vermieden werden.
Komposition besteht darin, ein Objekt des gewünschten Typs als Feld der Klasse einzuschließen und ihm die Arbeit zu delegieren, anstatt seine Implementierung zu erben. Kotlin erleichtert das Delegationsmuster mit dem Schlüsselwort by, das die automatische Delegation der Implementierung von Schnittstellen an Objekte ermöglicht.
Beispiel für das Delegationsmuster:
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("Aktion ausgeführt") } } fun main() { val service = Service(ConsoleLogger()) service.doAction() // Gibt aus: Aktion ausgeführt }
Dieser Ansatz vereinfacht die Wiederverwendbarkeit von Code und macht die Logik modularer.
"Kann eine data class von einer anderen Klasse erben, z.B. von einer abstrakten?"
data class in Kotlin kann nicht von einer anderen Klasse (außer Interfaces) erben, da data class immer final ist. Eine Ausnahme bilden Interfaces, die implementiert werden können.Beispiel:
abstract class Base(val name: String) data class Derived(val age: Int, val name: String) : Base(name) // Kompilierungsfehler: data class kann die Klasse Base nicht erweitern
Aber möglich ist:
interface User data class Admin(val name: String, val rights: List<String>) : User
Geschichte
Im Projekt wurde entschieden, mehrere Services von einer gemeinsamen abstrakten Klasse abzuleiten, um wiederkehrende Logik zu implementieren. Am Ende gab es viele Vererbungsebenen, die das Debugging erschwerten und Testprobleme verursachten. Nach dem Wechsel zu Komposition und Delegation (über Interfaces und Dependency Injection) konnte der Code vereinfacht, modularer gestaltet und die Testabdeckung erhöht werden.
Geschichte
Ein unerfahrener Entwickler versuchte, eine data class mithilfe einer anderen Klasse zu erweitern, um gemeinsame Funktionalität hinzuzufügen. Der Code kompiliert nicht, aber der Programmierer konnte lange Zeit den Grund nicht verstehen (Einschränkungen von data class in Kotlin).
Geschichte
In einem Projekt mit umfangreicher Logging-Logik wurde beschlossen, die Logging-Funktionalität in die Basisklasse auszulagern. Mit dem Wachstum des Systems benötigten einige Services jedoch eine andere Logging-Implementierung. Es musste refaktoriert werden, indem das Interface Logger und Komposition durch Delegation verwendet wurden, was die Architektur erheblich vereinfachte.