In Kotlin erstellt shallow copy (flaches Kopieren) ein neues Objekt, aber die eingebetteten Objekte (z.B. Elemente in Sammlungen oder Referenzfelder) werden nicht kopiert, sondern verweisen auf dieselben Objekte wie das ursprüngliche Exemplar. Deep copy (tiefes Kopieren) bedeutet rekursives Kopieren aller eingebetteten Objekte (Objektgraph), so dass das Original und die Kopie vollständig unabhängig sind.
In der Standardbibliothek von Kotlin gibt es kein universelles Mittel für tiefes Kopieren, und es muss manuell implementiert werden, insbesondere für komplexe Strukturen oder Graphen mit Zyklen.
data class Person(val name: String, val addresses: List<String>) val original = Person("Anna", listOf("Moskau", "London")) val shallow = original.copy() println(original.addresses === shallow.addresses) // true, dieselbe Referenz
data class Person(val name: String, val addresses: List<String>) { fun deepCopy(): Person = Person(name, addresses.toList()) } val deep = original.deepCopy() printfn(original.addresses === deep.addresses) // false, neue Kopie der Liste
Wird
copy()für data class mit eingebetteten Sammlungen verwendet, werden die Sammlungen kopiert? Warum?
Nein, copy() erstellt eine flache Kopie. Eingebettete Sammlungen und Objekte bleiben Referenzen auf dieselben Objekte. Änderungen an diesen Objekten sind sowohl im Original als auch in der Kopie sichtbar, was zu Bugs führen kann.
data class Box(val items: MutableList<String>) val box1 = Box(mutableListOf("A")) val box2 = box1.copy() box2.items.add("B") printfn(box1.items) // [A, B] — auch das Original wurde verändert!
Geschichte
In einem Projekt wurde der Zustand eines Objekts mittels des normalen
.copy()der data class gespeichert, in dem Glauben, einen "Schnappschuss" zu erhalten. Bei der Rückkehr zu einem alten Zustand stellte sich heraus, dass die eingebettete Liste (mutableList) in allen Kopien aufgrund des flachen Kopierens geändert worden war.
Geschichte
Im Dokumentensystem wurden Entitäten durch Serialisierung/Deserialisierung mit Gson kopiert, in der Erwartung, ein tiefes Kopieren zu erhalten, aber nullable Felder und Daten wurden falsch serialisiert — in der Folge traten inkorrekte Werte und Geschäftslogikfehler auf.
Geschichte
Bei der Migration des Zustands in einem Redux-ähnlichen Store für die Android-Entwicklung wurde
copy()für Zustandsbäume verwendet, in der Erwartung vollständiger Unabhängigkeit der Kopien. Letztendlich konnte ein Ast des Stores den Zustand eines anderen aufgrund von Referenzen auf dieselben internen mutierbaren Sammlungen verändern.