wake-up-neo.com

Die Kotlin-Datenklassen-Kopiermethode kopiert nicht alle Mitglieder tief

Könnte jemand erklären, wie genau die copy-Methode für Kotlin-Datenklassen funktioniert? Für manche Mitglieder scheint es so zu sein, dass eine (tiefe) Kopie nicht erstellt wird und die Verweise immer noch auf das Original verweisen.

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)

Ausgabe:
foo: Foo (a = 5, Balken = Balken (x = 0), Liste = [1, 2, 3])
foo: foo (a = 10, Balken = Balken (x = 2), Liste = [1, 2, 3, 4])
fooCopy: Foo (a = 5, Balken = Balken (x = 2), Liste = [1, 2, 3, 4])
barCopy: Bar (x = 0) 

Warum ist barCopy.x=0 (erwartet), aber fooCopy.bar.x=2 (ich denke, es wäre 0). Da Bar auch eine Datenklasse ist, würde ich erwarten, dass foo.bar auch eine Kopie ist, wenn foo.copy() ausgeführt wird.

Um alle Mitglieder tief zu kopieren, kann ich so etwas tun:

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

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

Aber vermisse ich etwas oder gibt es eine bessere Möglichkeit, dies zu tun, ohne angeben zu müssen, dass diese Mitglieder eine tiefe Kopie erzwingen müssen?

7
triad

Die copy-Methode von Kotlin soll keine tiefe Kopie sein. Wie im Referenzdokument ( https://kotlinlang.org/docs/reference/data-classes.html ) erläutert, für eine Klasse wie:

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

die copy-Implementierung wäre:

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

Wie Sie sehen, handelt es sich also um eine flache Kopie. Die Implementierungen von copy in Ihren speziellen Fällen wären:

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

Wie @Ekeko sagte, ist die für die Datenklasse implementierte Standardfunktion copy() eine flache Kopie, die wie folgt aussieht:

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

Um eine tiefe Kopie durchzuführen, müssen Sie die Funktion copy() überschreiben.

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

Es gibt eine Möglichkeit, eine tiefe Kopie eines Objekts in Kotlin (und Java) zu erstellen: serialisiert es in den Speicher und deserialisiert es dann zurück zu einem neuen Objekt. Dies funktioniert nur, wenn alle im Objekt enthaltenen Daten entweder Grundelemente sind oder die serialisierbare Schnittstelle implementieren

Hier eine Erklärung mit Kotlin-Beispielcode https://rosettacode.org/wiki/Deepcopy#Kotlin

Hinweis: Diese Lösung sollte auch in Android unter Verwendung der Parcelable-Schnittstelle anstelle von Serializable verwendet werden. Paketierbar ist effizienter.

0
Sebas LG

Aufbauend auf einer früheren Antwort ist die Verwendung der kotlinx.serialization - Funktion eine einfache, wenn auch etwas unelegante Lösung. Fügen Sie das Plugin gemäß der Dokumentation zu build.gradle Hinzu. Um dann eine tiefe Kopie eines Objekts zu erstellen, versehen Sie es mit @Serializable Und fügen Sie eine Kopiermethode hinzu, mit der das Objekt in eine serialisierte Binärform konvertiert wird wieder zurück. Das neue Objekt referenziert keine Objekte im Original.

import kotlinx.serialization.Serializable
import kotlinx.serialization.cbor.Cbor

@Serializable
data class DataClass(val yourData: Whatever, val yourList: List<Stuff>) {

    var moreStuff: Map<String, String> = mapOf()

    fun copy(): DataClass {
        return Cbor.load(serializer(), Cbor.dump(serializer(), this))
    }

Dies ist nicht so schnell wie eine handschriftliche Kopierfunktion, erfordert jedoch keine Aktualisierung, wenn das Objekt geändert wird, und ist daher robuster.

0
Clyde