如何在 Kotlin 中克隆对象?
How to clone object in Kotlin?
Kotlin documentation 仅在访问 Java 和枚举 class 中描述了克隆。在后一种情况下,克隆只是抛出一个异常。
那么,我将/应该如何克隆任意 Kotlin 对象?
我应该像 Java 那样使用 clone()
吗?
对于data class
,可以使用compiler-generatedcopy()
method。请注意,它将执行浅拷贝。
要创建集合的副本,请使用 toList()
或 toSet()
方法,具体取决于您需要的集合类型。这些方法总是创建一个集合的新副本;他们还执行浅拷贝。
对于其他 类,没有 Kotlin-specific 克隆解决方案。如果满足您的要求,您可以使用 .clone()
,如果不满足,您可以构建不同的解决方案。
您可以使用 Gson library 将原始对象转换为 String,然后将该 String 转换回实际的 Object 类型,您将得到一个克隆。虽然这不是实际用于 JSON 和其他对象类型之间转换的 Gson 库的预期用途,但我设计了这种方法来解决我的许多基于 Kotlin 的克隆问题 Android应用程序。
看我的例子。将此函数放在要创建克隆的 class/model 中。在我的示例中,我克隆了一个 Animal 类型的对象,所以我将把它放在 Animal class
中
class Animal{
fun clone(): Animal
{
val stringAnimal = Gson().toJson(this, Animal::class.java)
return Gson().fromJson<Animal>(stringAnimal, Animal::class.java)
}
}
然后像这样使用它:
val originalAnimal = Animal()
val clonedAnimal = originalAnimal.clone()
我已投票给@yole 的好答案,但如果您不(或不能)使用数据,我会投票给其他方式 class。你可以这样写辅助方法:
object ModelHelper {
inline fun <reified T : Serializable> mergeFields(from: T, to: T) {
from::class.java.declaredFields.forEach { field ->
val isLocked = field.isAccessible
field.isAccessible = true
field.set(to, field.get(from))
field.isAccessible = isLocked
}
}
}
因此您可以通过以下方式将实例 A“复制”到 B:
val bInstance = AClassType()
ModelHelper.mergeFields(aInstance, bInstance)
有时,我使用这种方式将来自多个实例的数据合并到一个对象中,该对象的值可用(不为空)。
它需要为您的 class 实现 Cloneable,然后将 clone() 重写为 public,例如:
public override fun clone(): Any {<your_clone_code>}
https://discuss.kotlinlang.org/t/how-to-use-cloneable/2364/3
如果您尝试克隆的 class 没有实现 Cloneable
或者不是数据 class 并且是外部库的一部分,您可以创建一个扩展方法returns 一个新实例。例如:
class Person {
var id: String? = null
var name: String? = null
}
fun Person.clone(): Person {
val person = Person()
person.id = id
person.name = name
return person
}
fun <T : Any> clone (obj: T): T {
if (!obj::class.isData) {
println(obj)
throw Error("clone is only supported for data classes")
}
val copy = obj::class.memberFunctions.first { it.name == "copy" }
val instanceParam = copy.instanceParameter!!
return copy.callBy(mapOf(
instanceParam to obj
)) as T
}
Kotlin data class
使用 .copy()
很容易克隆
所有值都会被浅拷贝,一定要小心处理任何list/array内容。
.copy()
的一个有用功能是能够在复制时更改任何值。有了这个 class:
data class MyData(
val count: Int,
val peanuts: Int?,
val name: String
)
val data = MyData(1, null, "Monkey")
您可以为任何属性设置值
val copy = data.copy(peanuts = 100, name = "Elephant")
copy
中的结果将具有值 (1, 100, "Elephant")
这是适用于任何对象类型的一致解决方案:
Kotlin 的 Array 数据结构提供了一个 clone() 方法,可以用来克隆数组的内容:
val a = arrayOf(1)
//Prints one object reference
println(a)
//Prints a different object reference
println(a.clone())
从 Kotlin 1.3 开始,所有主要目标都支持克隆方法,因此它应该可以跨平台使用。
也可以使用 kotlinx.serialization
克隆对象
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
@Serializable
class A
{
val name: String = "Cloneable class A"
fun clone(): A {
val json = Json(JsonConfiguration.Stable)
val jsonStr = json.stringify(serializer(), this)
return json.parse(serializer(), jsonStr)
}
}
Kotlin documentation 仅在访问 Java 和枚举 class 中描述了克隆。在后一种情况下,克隆只是抛出一个异常。
那么,我将/应该如何克隆任意 Kotlin 对象?
我应该像 Java 那样使用 clone()
吗?
对于data class
,可以使用compiler-generatedcopy()
method。请注意,它将执行浅拷贝。
要创建集合的副本,请使用 toList()
或 toSet()
方法,具体取决于您需要的集合类型。这些方法总是创建一个集合的新副本;他们还执行浅拷贝。
对于其他 类,没有 Kotlin-specific 克隆解决方案。如果满足您的要求,您可以使用 .clone()
,如果不满足,您可以构建不同的解决方案。
您可以使用 Gson library 将原始对象转换为 String,然后将该 String 转换回实际的 Object 类型,您将得到一个克隆。虽然这不是实际用于 JSON 和其他对象类型之间转换的 Gson 库的预期用途,但我设计了这种方法来解决我的许多基于 Kotlin 的克隆问题 Android应用程序。 看我的例子。将此函数放在要创建克隆的 class/model 中。在我的示例中,我克隆了一个 Animal 类型的对象,所以我将把它放在 Animal class
中class Animal{
fun clone(): Animal
{
val stringAnimal = Gson().toJson(this, Animal::class.java)
return Gson().fromJson<Animal>(stringAnimal, Animal::class.java)
}
}
然后像这样使用它:
val originalAnimal = Animal()
val clonedAnimal = originalAnimal.clone()
我已投票给@yole 的好答案,但如果您不(或不能)使用数据,我会投票给其他方式 class。你可以这样写辅助方法:
object ModelHelper {
inline fun <reified T : Serializable> mergeFields(from: T, to: T) {
from::class.java.declaredFields.forEach { field ->
val isLocked = field.isAccessible
field.isAccessible = true
field.set(to, field.get(from))
field.isAccessible = isLocked
}
}
}
因此您可以通过以下方式将实例 A“复制”到 B:
val bInstance = AClassType()
ModelHelper.mergeFields(aInstance, bInstance)
有时,我使用这种方式将来自多个实例的数据合并到一个对象中,该对象的值可用(不为空)。
它需要为您的 class 实现 Cloneable,然后将 clone() 重写为 public,例如:
public override fun clone(): Any {<your_clone_code>}
https://discuss.kotlinlang.org/t/how-to-use-cloneable/2364/3
如果您尝试克隆的 class 没有实现 Cloneable
或者不是数据 class 并且是外部库的一部分,您可以创建一个扩展方法returns 一个新实例。例如:
class Person {
var id: String? = null
var name: String? = null
}
fun Person.clone(): Person {
val person = Person()
person.id = id
person.name = name
return person
}
fun <T : Any> clone (obj: T): T {
if (!obj::class.isData) {
println(obj)
throw Error("clone is only supported for data classes")
}
val copy = obj::class.memberFunctions.first { it.name == "copy" }
val instanceParam = copy.instanceParameter!!
return copy.callBy(mapOf(
instanceParam to obj
)) as T
}
Kotlin data class
使用 .copy()
所有值都会被浅拷贝,一定要小心处理任何list/array内容。
.copy()
的一个有用功能是能够在复制时更改任何值。有了这个 class:
data class MyData(
val count: Int,
val peanuts: Int?,
val name: String
)
val data = MyData(1, null, "Monkey")
您可以为任何属性设置值
val copy = data.copy(peanuts = 100, name = "Elephant")
copy
中的结果将具有值 (1, 100, "Elephant")
这是适用于任何对象类型的一致解决方案:
Kotlin 的 Array 数据结构提供了一个 clone() 方法,可以用来克隆数组的内容:
val a = arrayOf(1)
//Prints one object reference
println(a)
//Prints a different object reference
println(a.clone())
从 Kotlin 1.3 开始,所有主要目标都支持克隆方法,因此它应该可以跨平台使用。
也可以使用 kotlinx.serialization
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
@Serializable
class A
{
val name: String = "Cloneable class A"
fun clone(): A {
val json = Json(JsonConfiguration.Stable)
val jsonStr = json.stringify(serializer(), this)
return json.parse(serializer(), jsonStr)
}
}