ProgrammationDéveloppeur Kotlin

Expliquez la différence entre shallow copy et deep copy en Kotlin. Comment réaliser une copie profonde des objets et quelles sont les erreurs typiques lors de la copie de structures complexes ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

En Kotlin, shallow copy (copie superficielle) crée un nouvel objet, mais les objets imbriqués (par exemple, les éléments des collections ou les champs de référence) ne sont pas copiés, mais référencent les mêmes objets que l'instance d'origine. Deep copy (copie profonde) signifie copier récursivement tous les objets imbriqués (graphe d'objets), afin que l'original et la copie soient entièrement indépendants.

Il n'existe pas de moyen universel dans la bibliothèque standard de Kotlin pour effectuer une copie profonde, et il est nécessaire de l'implémenter manuellement, en particulier pour des structures complexes ou des graphes avec des cycles.

Exemple de copie superficielle :

data class Person(val name: String, val addresses: List<String>) val original = Person("Anna", listOf("Moscow", "London")) val shallow = original.copy() println(original.addresses === shallow.addresses) // true, même référence

Exemple de copie profonde :

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, nouvelle copie de la liste

Question piège.

Si on utilise .copy() pour un data class avec des collections imbriquées, les collections seront-elles copiées ? Pourquoi ?

Réponse avec exemple :

Non, copy() crée une copie superficielle. Les collections et objets imbriqués resteront des références aux mêmes objets. Toute modification dans ces objets sera visible à la fois dans l'original et dans la copie, ce qui peut entraîner des bugs.

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] — l'original a été modifié !

Exemples d'erreurs réelles dues à une méconnaissance des subtilités du sujet.


Histoire

Dans un projet, on a sauvegardé l'état d'un objet via le .copy() d'un data class, pensant obtenir un "instantané". En revenant à un ancien état, il s'est avéré que la liste imbriquée (mutableList) avait été modifiée dans toutes les copies en raison de la copie superficielle.


Histoire

Dans un système de documents, on a copié des entités via sérialisation/désérialisation avec Gson, s'attendant à une copie profonde, mais les champs nullable et les dates étaient mal sérialisés — le résultat était des valeurs incorrectes et des erreurs de logique métier.


Histoire

Lors de la migration d'état dans un store de type Redux pour le développement Android, on a commencé à utiliser copy() pour des arbres d'état, s'attendant à une indépendance totale des copies. Au final, une branche du store pouvait muter l'état d'une autre en raison de références aux mêmes collections mutables internes.