ProgrammazioneSviluppatore Kotlin

Spiega la differenza tra shallow copy e deep copy in Kotlin. Come implementare la copia profonda degli oggetti e quali sono gli errori tipici nella copia di strutture complesse?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

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.

Esempio di copia superficiale:

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

Esempio di copia profonda:

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

Domanda con inganno.

Se si utilizza .copy() per una data class con collezioni annidate, le collezioni verranno copiate? Perché?

Risposta con esempio:

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!

Esempi di errori reali dovuti all'ignoranza delle sottigliezze dell'argomento.


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.