Kotlin ondersteunt parameters voor overerving met het sleutelwoord open, maar de belangrijkste aanbeveling van de taal is om compositie boven overerving te verkiezen. Dit stelt je in staat om meer flexibele, uitbreidbare systemen te creëren, waarbij je de kwetsbaarheid van hiërarchieën en problemen die verband houden met diepe overerving vermijdt.
Compositie houdt in dat je een object van het vereiste type als een veld van de klasse opneemt en het zijn werk laat doen in plaats van de implementatie over te nemen. Kotlin vergemakkelijkt het delegatiepatroon met behulp van het sleutelwoord by, waardoor je automatisch de implementatie van interfaces aan objecten kunt delegeren.
Voorbeeld van het delegatiepatroon:
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("Actie voltooid") } } fun main() { val service = Service(ConsoleLogger()) service.doAction() // Geeft weer: Actie voltooid }
Deze aanpak vereenvoudigt hergebruik van code en maakt de logica meer modulair.
"Kan een data class van een andere klasse erven, bijvoorbeeld van een abstracte?"
data class in Kotlin kan niet van een andere klasse erven (behalve interfaces), omdat data class altijd final is. Uitzonderingen zijn interfaces; die kunnen worden geïmplementeerd.Voorbeeld:
abstract class Base(val name: String) data class Derived(val age: Int, val name: String) : Base(name) // Compilatiefout: data class kan niet uitbreiden naar de klasse Base
Maar het is mogelijk:
interface User data class Admin(val name: String, val rights: List<String>) : User
Verhaal
In een project werd besloten om verschillende diensten van een gemeenschappelijke abstracte klasse te laten erven om herhalende logica te implementeren. Dit resulteerde in veel niveaus van overerving, wat het debuggen bemoeilijkte en problemen met testen met zich meebracht. Na de overstap naar compositie en delegatie (via interfaces en afhankelijkheidinjectie) lukte het om de code te vereenvoudigen, deze meer modulair te maken en de testdekking te verhogen.
Verhaal
Een beginnende ontwikkelaar probeerde een data class uit te breiden met behulp van een andere klasse om gemeenschappelijke functionaliteit toe te voegen. De code compileerde niet, maar de programmeur kon lange tijd de reden niet begrijpen (beperkingen van data class in Kotlin).
Verhaal
In een project met uitgebreide logica voor logging werd besloten om de loggingfunctionaliteit naar een basis klasse te verplaatsen. Echter, met de groei van het systeem vereisten sommige diensten een andere implementatie van logging. Het was nodig om te refactoren met behulp van de interface Logger en compositie via delegatie, wat de architectuur aanzienlijk vereenvoudigde.