ProgrammatieAndroid ontwikkelaar

Beschrijf de verschillen tussen abstracte klassen en interfaces in Kotlin. Wanneer moet welke aanpak worden toegepast, welke implementatiekenmerken zijn er in de taal en wat zijn de nuances van overerving?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord.

In Kotlin worden abstracte klassen (abstract class) en interfaces (interface) gebruikt om abstracte logica te declareren, maar er zijn enkele belangrijke verschillen:

  • Abstracte klasse kan toestanden (velden), implementaties van methoden en abstracte methoden bevatten. Een klasse kan slechts één abstracte (of gewone) klasse erven. Abstracte klassen kunnen constructeurs hebben.
  • Interface kan property-declaraties bevatten (zonder toestanden op te slaan!), standaardimplementaties van methoden en abstracte methoden. Sinds Kotlin 1.1 kan een interface property-implementaties bevatten, maar ze kunnen geen toestanden hebben. Een klasse kan meerdere interfaces implementeren.
  • Interfaces kunnen geen toestanden (backing field) bevatten, terwijl abstracte klassen dit wel kunnen.

Voorbeeldcode

abstract class Animal(val name: String) { abstract fun makeSound() fun info() = println("Ik ben $name") } interface Movable { fun move() } class Cat(name: String) : Animal(name), Movable { override fun makeSound() = println("Miauw") override fun move() = println("Kat beweegt") }

Misleidende vraag.

Kan een interface in Kotlin toestanden (backing field) bevatten?

De meeste mensen antwoorden dat je properties in een interface kunt declareren, maar ze hebben nog steeds geen backing field — alleen getters/setters zonder waardeopslag. Zelfs als je get() = ... schrijft, wordt de waarde elke keer opnieuw berekend.

Voorbeeld:

interface MyInterface { val value: Int get() = 42 // geen waardeopslag, berekening on-the-fly }

Voorbeelden van echte fouten door gebrek aan kennis van de nuances van het onderwerp.


Verhaal

In een groot project probeerde een senior ontwikkelaar de toestand in een interface op te slaan (bijvoorbeeld een teller), in de veronderstelling dat de property var count: Int een backing field zou hebben. Dit resulteerde in inconsistent gedrag in alle klassen die de interface implementeerden, omdat ze waardeopslag op verschillende manieren moesten implementeren, wat leidde tot instabiele logica (gegevens gingen verloren als ze vergaten de setter te overschrijven).


Verhaal

Een van de ontwikkelaars gebruikte een abstracte klasse waar meerdere implementaties van verschillende gedragsstrategieën nodig waren (strategiepatroon). Dit leidde tot een probleem: er kon slechts één basisklasse per hiërarchie zijn, maar voor verschillende aspecten van gedrag was compositie nodig. De architectuur moest worden gewijzigd naar interfaces.


Verhaal

Het team probeerde op hetzelfde moment twee abstracte klassen te erven (één met logica, de andere met algemene hulpfuncties), wat onmogelijk is in Kotlin. Het probleem kwam al aan het licht in de fase van het uitbreiden van de applicatie. Dit leidde tot aanzienlijke technische schuld, omdat de klassen snel moesten worden geherstructureerd.