有人能解释一下Kotlin数据类的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) fooCopy: Foo(a=5,bar=Bar(x=2),list=1,2,3,4) barCopy: Bar(x=0)
为什么是barCopy.x=0
(预期),但是fooCopy.bar.x=2
(我认为应该是0)。因为Bar
也是一个数据类,所以在执行foo.copy()
时,foo.bar
也是一个副本。
为了深入复制所有成员,我可以这样做:
val fooCopy = foo.copy(bar = foo.bar.copy(), list = foo.list.toMutableList())
fooCopy: Foo(a=5,bar=Bar(x=0),list=1,2,3)
但是,我是否遗漏了一些东西,或者是否有更好的方法来做到这一点,而无需具体说明这些成员需要强制提交一份深刻的副本?
发布于 2017-11-18 00:27:57
Kotlin的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)
发布于 2017-11-18 09:57:41
正如@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)
发布于 2019-01-12 08:36:21
有一种方法可以在Kotlin (和Java)中对对象进行深度复制:将其序列化为内存,然后反序列化它回到一个新对象。这只有在对象中包含的所有数据都是原语或实现可序列化接口时才能工作。
下面是使用示例Kotlin代码https://rosettacode.org/wiki/Deepcopy#Kotlin的说明
import java.io.Serializable
import java.io.ByteArrayOutputStream
import java.io.ByteArrayInputStream
import java.io.ObjectOutputStream
import java.io.ObjectInputStream
fun <T : Serializable> deepCopy(obj: T?): T? {
if (obj == null) return null
val baos = ByteArrayOutputStream()
val oos = ObjectOutputStream(baos)
oos.writeObject(obj)
oos.close()
val bais = ByteArrayInputStream(baos.toByteArray())
val ois = ObjectInputStream(bais)
@Suppress("unchecked_cast")
return ois.readObject() as T
}
注意:这个解决方案也应该适用于Android,使用Parcelable界面而不是Serializable。Parcelable更有效。
https://stackoverflow.com/questions/47359496
复制相似问题