提问者:小点点

静态编程语言数据类复制方法不深度复制所有成员


有人能解释一下静态编程语言数据类的copy方法是如何工作的吗?对于一些成员来说,似乎没有真正创建(深度)副本,并且引用仍然是原始的。

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)

输出:
foo: Foo(a=5,bar=Bar(x=0),list=[1,2,3])
foo:Foo(a=10,bar=Bar(x=2),list=[1,2,3,4])
foCopy:Foo(a=5,bar=Bar(x=2),list=[1,2,3,4])
bar Copy:Bar(x=0)

为什么barCopy. x=0(符合预期),但fooCopy.bar.x=2(我认为它会是0)。由于Bar也是一个数据类,我希望foo.bar在执行foo.copy()时也是一个副本。

要深拷贝所有成员,我可以这样做:

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

foCopy:Foo(a=5,bar=Bar(x=0),list=[1,2,3])

但是我遗漏了什么,或者有没有更好的方法来做到这一点,而不需要指定这些成员需要强制深拷贝?


共3个答案

匿名用户

静态编程语言的copy方法根本不应该是深度副本。如参考文档(https://kotlinlang.org/docs/reference/data-classes.html)中所述,对于诸如:

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

copy实现将是:

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

如您所见,这是一个浅层副本。copy在您的特定情况下的实现是:

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)

匿名用户

当心那些只是将列表引用从旧对象复制到新对象的答案。深度复制的一种快速方法(虽然不是很有效)是序列化/反序列化对象,即将对象转换为JSON,然后将它们转换回POJO。如果你使用的是GSON,这里有一段快速代码:

class Foo {
    fun deepCopy() : Foo {
        return Gson().fromJson(Gson().toJson(this), this.javaClass)
    }
}

匿名用户

正如@Ekeko所说,为数据类实现的默认copy()函数是一个浅拷贝,如下所示:

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

要执行深度复制,您必须覆盖copy()函数。

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