ProgrammatieMiddle Android ontwikkelaar

Op welke manier is onveranderlijkheid (immutability) van collecties en objecten geïmplementeerd in Kotlin, en hoe verschilt de praktijk van die in Java?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord

Geschiedenis van de kwestie:

In Java wordt onveranderlijkheid van collecties bereikt via wrappers zoals Collections.unmodifiableList() en het handmatig ontwerpen van immutable-klassen (final-velden, geen setters). In de praktijk van Android- en serverontwikkeling werd dit omslachtig en onhandig, en de threadveiligheid en voorspelbaarheid van gegevens gingen verloren.

Probleem:

Java-collecties zijn standaard niet veilig; je kunt altijd een referentie krijgen en het object wijzigen. Dit bemoeilijkt de controle over onveranderlijkheid, vooral in multithread-omgevingen, en leidt tot moeilijk te vangen bugs.

Oplossing:

Kotlin is oorspronkelijk ontworpen met een scheiding tussen read-only en mutabele collecties, en het creëren van werkelijk immutable objecten werd eenvoudiger met behulp van val, data class en List/Set/Map zonder de mutable-prefix. Bovenop dit alles — het gebruik van kopiëren via copy()-methoden.

Voorbeeldcode:

val nums: List<Int> = listOf(1, 2, 3) // onveranderlijke lijst // nums.add(4) // compilatiefout val user = User(name = "Alex", age = 30) val updated = user.copy(age = 31) // Voorbeeld met onveranderlijke map val state: Map<String, Int> = mapOf("a" to 1, "b" to 2)

Belangrijke kenmerken:

  • Compilatie bij het proberen om een read-only collectie te wijzigen zal een foutmelding geven
  • In Kotlin garandeert val de onveranderlijkheid van de referentie, maar niet noodzakelijk van het object
  • Voor werkelijk immutable — gebruik alleen read-only interfaces en verbied wijziging van interne toestand

Vragen met een valstrik.

Garandeert val absolute onveranderlijkheid van het interne object?

Nee! val garandeert alleen dat de variabele niet naar een ander object kan verwijzen, maar de velden van het object kunnen worden gewijzigd (als de interne eigenschappen var zijn).

Is het mogelijk om een mutabel object te krijgen uit een read-only collectie?

Ja — als de collectie een mutabel object bevat, kun je zijn toestand wijzigen, ondanks de read-only interface van de collectie.

data class User(var name: String) val users: List<User> = listOf(User("Vasya")) users[0].name = "Petya" // toegestaan

Wat gebeurt er bij het casten van List<Int> naar MutableList<Int> via as?

Het compileert, maar zal leiden tot ClassCastException tijdens runtime, als het oorspronkelijke type een read-only interface was.

Typische fouten en anti-patronen

  • Het aanvaarden van val als absolute garantie voor de onveranderlijkheid van een object
  • Manipulatie van referenties naar mutabele objecten binnen "immutable" collecties
  • Casten van read-only collecties naar mutabele

Voorbeeld uit het leven

Negatieve case

Een Android-applicatie slaat state op in val List<User>, denkend dat het onveranderlijk is. Een van de ontwikkelaars wijzigt user.name. In een ander fragment toont de bijgewerkte lijst plotseling inconsistente gegevens — een bug verschijnt in de release.

Voordelen:

  • Snelle werking met collecties

Nadelen:

  • Verlies van controle over mutaties
  • Verborgen mutabiliteit

Positieve case

Gebruik van immutable data class alleen met val-velden, filtratie van toegang tot interne objecten, bijwerken van toestand via copy().

Voordelen:

  • Hoge voorspelbaarheid en threadveiligheid
  • Geen onverwachte neveneffecten

Nadelen:

  • Vereist soms meer allocaties en kopieën
  • Niet alle externe bibliotheken bieden werkelijk immutable objecten aan