如何对使用属性委托的Kotlin对象实现深度克隆
适配Kotlin属性委托场景的深度克隆解决方案
Apache BeanUtils 是基于Java反射实现的工具,无法识别Kotlin特有的属性委托语法结构,因此不适用于该场景,可选择以下3种落地性强的方案:
方案1:使用kotlinx.serialization序列化反序列化实现(最稳定推荐)
原理是通过Kotlin官方序列化框架将对象转为二进制流,再反序列化为全新对象,原生兼容属性委托、内部类等Kotlin专属语法特性,不会出现属性读取异常。
实现步骤
- 给所有参与克隆的类(包括外层类、内部类、自定义委托类)添加
@Serializable注解:
import kotlinx.serialization.Serializable @Serializable class Model { val p1: String by Delegate() @Serializable inner class InnerModel { val p2: String by Delegate() } val m = InnerModel() } // 自定义委托类也需要实现序列化 @Serializable class Delegate<T> : ReadWriteProperty<Any?, T> { // 原有委托逻辑保持不变 }
- 实现通用深度克隆扩展方法:
import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.decodeFromByteArray import kotlinx.serialization.encodeToByteArray inline fun <reified T> T.deepClone(): T { val bytes = ProtoBuf.encodeToByteArray(this) return ProtoBuf.decodeFromByteArray(bytes) }
- 调用效果完全符合预期:
val m1 = Model() val m2 = m1.deepClone() m1.m.p2 = "asd" assertNotEquals(m1.m.p2, m2.m.p2) // 断言正常通过
方案2:自定义Kotlin反射实现克隆
如果无法给业务类添加序列化注解,可以通过Kotlin反射遍历所有属性(包括委托属性的后备存储字段)手动完成复制:
import kotlin.reflect.full.declaredMemberProperties import kotlin.reflect.jvm.isAccessible fun Any.deepClone(): Any { // 不可变基础类型直接返回 val clazz = this::class.java if (clazz.isPrimitive || this is String || this is Number || this is Boolean) { return this } // 处理集合类型 if (this is Collection<*>) return this.map { it?.deepClone() } if (this is Map<*, *>) return this.mapValues { it.value?.deepClone() } // 反射创建类实例 val constructor = this::class.constructors.first { it.parameters.isEmpty() } constructor.isAccessible = true val newInstance = constructor.call() // 复制所有属性值(包含委托属性) this::class.declaredMemberProperties.forEach { prop -> prop.isAccessible = true val clonedValue = prop.get(this)?.deepClone() if (prop is kotlin.reflect.KMutableProperty<*>) { prop.setter.call(newInstance, clonedValue) } } return newInstance } inline fun <reified T> T.clone(): T = deepClone() as T
方案3:使用Kotlin专属克隆库
直接使用针对Kotlin特性开发的克隆库如klone,这类库原生支持属性委托、内部类、数据类的深度克隆,无需额外配置,引入依赖后直接调用klone()方法即可实现需求。
通用注意事项
如果你的自定义委托类持有可变状态,需要确保克隆过程中委托实例本身也被深度复制,而非仅复制引用,否则依然会出现新旧对象属性联动修改的问题。
内容的提问来源于stack exchange,提问作者Nikita Chegodaev
相关产品推荐
相关产品推荐

