web-dev-qa-db-fra.com

La méthode de copie de classe de données Kotlin ne copie pas en profondeur tous les membres

Quelqu'un pourrait-il expliquer comment fonctionne exactement la méthode copy pour les classes de données Kotlin? Il semble que pour certains membres, une copie (profonde) n’est pas créée et les références sont toujours à l’original.

fun test() {
    val bar = Bar(0)
    val foo = Foo(5, bar, mutableListOf(1, 2, 3))
    println("foo    : $foo")

    val barCopy = bar.copy()
    val fooCopy = foo.copy()
    foo.a = 10
    bar.x = 2
    foo.list.add(4)

    println("foo    : $foo")
    println("fooCopy: $fooCopy")
    println("barCopy: $barCopy")
}

data class Foo(var a: Int,
               val bar: Bar,
               val list: MutableList<Int> = mutableListOf())

data class Bar(var x: Int = 0)

Sortie:
foo: Foo (a = 5, barre = barre (x = 0), liste = [1, 2, 3])
foo: Foo (a = 10, barre = barre (x = 2), liste = [1, 2, 3, 4])
fooCopy: Foo (a = 5, barre = barre (x = 2), liste = [1, 2, 3, 4])
barCopy: Barre (x = 0) 

Pourquoi barCopy.x=0 (attendu), mais fooCopy.bar.x=2 (je pense que ce serait 0). Puisque Bar est également une classe de données, je m'attendrais à ce que foo.bar soit également une copie lorsque foo.copy() est exécuté.

Pour copier en profondeur tous les membres, je peux faire quelque chose comme ceci:

val fooCopy = foo.copy(bar = foo.bar.copy(), list = foo.list.toMutableList())

fooCopy: Foo (a = 5, barre = barre (x = 0), liste = [1, 2, 3])

Mais manque-t-il quelque chose ou existe-t-il un meilleur moyen de le faire sans avoir à préciser que ces membres doivent forcer une copie conforme?

7
triad

La méthode copy de Kotlin n'est pas censée être une copie profonde. Comme expliqué dans la documentation de référence ( https://kotlinlang.org/docs/reference/data-classes.html ), pour une classe telle que:

data class User(val name: String = "", val age: Int = 0)

la mise en œuvre copy serait:

fun copy(name: String = this.name, age: Int = this.age) = User(name, age)

Donc, comme vous pouvez le voir, c'est une copie superficielle. Les implémentations de copy dans vos cas spécifiques seraient:

fun copy(a: Int = this.a, bar: Bar = this.bar, list: MutableList<Int> = this.list) = Foo(a, bar, list)

fun copy(x: Int = this.x) = Bar(x)
25
Ekeko

Comme @Ekeko l'a dit, la fonction copy() implémentée pour la classe de données est une copie superficielle qui ressemble à ceci:

fun copy(a: Int = this.a, bar: Bar = this.bar, list: MutableList<Int> = this.list)

Pour effectuer une copie en profondeur, vous devez remplacer la fonction copy().

fun copy(a: Int = this.a, bar: Bar = this.bar.copy(), list: MutableList<Int> = this.list.toList()) = Foo(a, bar, list)
2
CrazyApple

Il existe un moyen de copier en profondeur un objet dans Kotlin (et Java): sérialisez-le en mémoire, puis désérialisez-le en un nouvel objet. Cela ne fonctionnera que si toutes les données contenues dans l'objet sont des primitives ou implémentent l'interface Serializable.

Voici une explication avec un exemple de code Kotlin https://rosettacode.org/wiki/Deepcopy#Kotlin

Remarque: cette solution devrait également s'appliquer à Android en utilisant l'interface Parcelable au lieu de Serializable. Colisable est plus efficace.

0
Sebas LG