In Kotlin, shallow copy (copia superficiale) crea un nuovo oggetto, ma gli oggetti annidati (ad esempio, gli elementi delle collezioni o i campi di riferimento) non vengono copiati, ma fanno riferimento agli stessi oggetti dell'istanza originale. Deep copy (copia profonda) significa copiare ricorsivamente tutti gli oggetti annidati (grafico degli oggetti), in modo che l'originale e la copia siano completamente indipendenti.
Nella libreria standard di Kotlin non esiste uno strumento universale per la copia profonda, e deve essere implementato manualmente, soprattutto per strutture complesse o grafi con cicli.
data class Person(val name: String, val addresses: List<String>) val original = Person("Anna", listOf("Mosca", "Londra")) val shallow = original.copy() println(original.addresses === shallow.addresses) // true, stesso riferimento
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, nuova copia della lista
Se si utilizza
.copy()per una data class con collezioni annidate, le collezioni verranno copiate? Perché?
No, copy() crea una copia superficiale. Le collezioni e gli oggetti annidati rimarranno riferimenti agli stessi oggetti. Qualsiasi modifica a questi oggetti sarà visibile sia nell'originale che nella copia, il che potrebbe portare a bug.
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] — anche l'originale è stato modificato!
Storia
Nel progetto si salvava lo stato dell'oggetto tramite un normale
.copy()di una data class, credendo di ottenere uno "snapshot". Tornando a uno stato precedente, si scopriva che la lista annidata (mutableList) era stata modificata in tutte le copie a causa della copia superficiale.
Storia
Nel sistema di documenti si copiavano le entità tramite serializzazione/deserializzazione con Gson, aspettandosi una copia profonda, ma i campi nullable e le date venivano serializzati in modo errato — il risultato erano valori non corretti e errori nella logica aziendale.
Storia
Durante la migrazione dello stato in uno store simile a Redux per lo sviluppo di Android, si è cominciato ad utilizzare
copy()per gli alberi di stato, aspettandosi una totale indipendenza delle copie. Alla fine, un ramo dello store poteva mutare lo stato di un altro a causa dei riferimenti alle stesse collezioni mutable interne.