Die Geschichte der Frage:
In Java wird Unveränderlichkeit von Sammlungen durch Wrapper wie Collections.unmodifiableList() und die manuelle Gestaltung von immutable-Klassen (final-Felder, keine Setter) erreicht. In der Praxis der Android- und Serverentwicklung wurde dies umständlich und unbequem, und die Thread-Sicherheit sowie die Vorhersehbarkeit der Daten gingen verloren.
Das Problem:
Java-Sammlungen sind standardmäßig nicht sicher, man kann immer auf ein Objekt zugreifen und es ändern. Dies erschwert die Kontrolle über die Änderbarkeit, insbesondere in Multithread-Umgebungen, und führt zu schwer fassbaren Bugs.
Die Lösung:
Kotlin wurde von Anfang an mit einer Trennung von read-only und mutable Sammlungen entworfen, und die Erstellung von wirklich unveränderlichen Objekten wurde einfacher durch die Verwendung von val, data class und List/Set/Map ohne mutable-Präfix. Darüber hinaus wird das Kopieren über copy()-Methoden verwendet.
Beispielcode:
val nums: List<Int> = listOf(1, 2, 3) // unveränderliche Liste // nums.add(4) // Kompilierungsfehler val user = User(name = "Alex", age = 30) val updated = user.copy(age = 31) // Beispiel mit unveränderlichem map val state: Map<String, Int> = mapOf("a" to 1, "b" to 2)
Wesentliche Merkmale:
Garantiert val die absolute Unveränderlichkeit eines verschachtelten Objekts?
Nein! val garantiert nur, dass die Variable nicht auf ein anderes Objekt verweisen kann, aber die Felder des Objekts können verändert werden (wenn interne Eigenschaften var sind).
Kann man ein veränderbares Objekt aus einer read-only Sammlung erhalten?
Ja — wenn die Sammlung ein veränderbares Objekt enthält, kann dessen Zustand geändert werden, trotz der read-only Schnittstelle der Sammlung.
data class User(var name: String) val users: List<User> = listOf(User("Vasya")) users[0].name = "Petya" // erlaubt
Was passiert bei der Umwandlung von List<Int> zu MutableList<Int> über as?
Es wird kompiliert, führt jedoch zur ClassCastException zur Laufzeit, wenn der ursprüngliche Typ eine read-only Schnittstelle war.
Eine Android-Anwendung speichert den Zustand in val List<User> und betrachtet ihn als unveränderlich. Einer der Entwickler ändert user.name. In einem anderen Fragment zeigt die aktualisierte Liste unerwartet inkonsistente Daten an — ein Fehler tritt im Release auf.
Vorteile:
Nachteile:
Verwendung von immutable data class nur mit val-Feldern, Filterung des Zugriffs auf verschachtelte Objekte, Aktualisierung des Zustands über copy().
Vorteile:
Nachteile: