W Kotlinie, shallow copy (powierzchowne kopiowanie) tworzy nowy obiekt, ale zagnieżdżone obiekty (np. elementy kolekcji lub pola-referencje) nie są kopiowane, a odnoszą się do tych samych obiektów co oryginalny egzemplarz. Deep copy (głębokie kopiowanie) oznacza rekurencyjne kopiowanie wszystkich zagnieżdżonych obiektów (grafu obiektów), tak aby oryginał i kopia były całkowicie niezależne.
W standardowej bibliotece Kotlin nie ma uniwersalnego narzędzia do głębokiego kopiowania, więc trzeba to zaimplementować ręcznie, szczególnie dla złożonych struktur lub grafów z cyklami.
data class Person(val name: String, val addresses: List<String>) val original = Person("Anna", listOf("Moskwa", "Londyn")) val shallow = original.copy() println(original.addresses === shallow.addresses) // true, ten sam odnośnik
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, nowa kopia listy
Jeśli użyjesz
.copy()dla klasy data z wewnętrznymi kolekcjami, czy kolekcje zostaną skopiowane? Dlaczego?
Nie, copy() tworzy powierzchowną kopię. Zagnieżdżone kolekcje i obiekty pozostaną referencjami do tych samych obiektów. Jakiekolwiek zmiany w tych obiektach będą widoczne zarówno w oryginale, jak i w kopii, co może prowadzić do błędów.
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] — zmodyfikowano również oryginał!
Historia
W projekcie przechowywano stan obiektu za pomocą zwykłego
.copy()klasy data, sądząc, że uzyskuje się "zdjęcie". Przy powrocie do starego stanu okazało się, że zagnieżdżona lista (mutableList) została zmieniona we wszystkich kopiach z powodu powierzchownego kopiowania.
Historia
W systemie dokumentów kopiowano encje przez serializację/deserializację za pomocą Gson, spodziewając się głębokiego kopiowania, ale pola nullable i daty były serializowane niepoprawnie — w rezultacie pojawiały się nieprawidłowe wartości i błędy logiki biznesowej.
Historia
Przy migracji stanu w sklepie podobnym do Redux dla deweloperów Androida zaczęto używać
copy()dla drzew stanu, oczekując pełnej niezależności kopii. W rezultacie jedna gałąź sklepu mogła mutować stan innej z powodu referencji do tych samych wewnętrznych kolekcji mutable.