ProgrammatieBackend ontwikkelaar

Hoe zijn generics in Kotlin geïmplementeerd? Welke beperkingen zijn er, hoe werkt invariantiestelling, covariantie en contravariantie, en hoe verschillen ze van generics in Java? Geef voorbeelden van gebruik.

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord

Generics in Kotlin stellen ons in staat om universele en type-veilige datastructuren en functies te creëren. De belangrijkste eigenschap: Kotlin implementeert een systeem van generic types op compilatieniveau, net als Java, maar met strengere type-definities en een uitgebreide syntaxis voor variatie (covariantie en contravariantie).

Beperkingen van generics:

  • Type-parameters zijn standaard invariant.
  • Het is niet mogelijk om een instantie van een type-parameter te creëren (T() is niet toegestaan).
  • Er is geen toegang tot type-informatie van generics tijdens runtime (type erasure).

Variantie:

  • Covariantie (out T): staat het gebruik van subtypes toe.
  • Contravariantie (in T): staat het gebruik van supertypes toe.
  • Invariantie: type zonder variatie-modifier.

Voorbeeld van covariantie:

interface Producer<out T> { fun produce(): T }

Voorbeeld van contravariantie:

interface Consumer<in T> { fun consume(item: T) }

Verschil met Java:

  • De syntaxis is duidelijker en beknopter (out in plaats van ? extends, in in plaats van ? super).
  • Er zijn geen wildcard-types (?, alleen in/out).
  • Verbod op het creëren van een instantie van een generic-parameter.

Misleidende vraag

Vraag: "Kun je in Kotlin een array van arrays (Array<Array<Int>>) verklaren als Array<out Array<Int>> en wat gebeurt er bij een poging om in zo'n array te schrijven?"

Antwoord: Ja, je kunt het verklaren als Array<out Array<Int>>, maar zo'n array wordt alleen-lezen (read-only):

val arr: Array<out Array<Int>> = Array(1) { Array(1) { 0 } } arr[0] = arrayOf(1, 2, 3) // Compilatiefout!

Een poging om een waarde te schrijven zal een fout veroorzaken — een generic array met een out-parameter staat geen elementen toe om te schrijven, omdat dit de typeveiligheid zou schenden.

Voorbeelden van echte foutmeldingen door onwetendheid over de details van het onderwerp


Verhaal

Het team probeerde een array van generic-objecten met een out-parameter-type te maken en vervolgens waarden erin te plaatsen via set(index, value). De code compileerde, maar veroorzaakte een fout tijdens runtime, en verschillende functies werkten niet meer.


Verhaal

Een keer, bij de migratie van een bibliotheek van Java naar Kotlin, bleven wildcard-types (? extends ...), terwijl we in Kotlin de types gewoon kopieerden zonder deze te veranderen naar out/in. Het resultaat — de compilatie faalde, en bij het "doorlopen" trad een fout op tijdens runtime, wat het debugproces bemoeilijkte.


Verhaal

We gebruikten in/out variatie met een gebruikersklasse, maar verwisselden de modifiers, door interface Stack<in T> in plaats van Stack<out T> te verklaren. Dit leidde tot het onvermogen om elementen uit de stack terug te geven: de handtekening van de methode schond het system out/in-contract.