如何为已适配kotlinx.serialization的Kotlin类实现自定义Java序列化
解决方案
优先方案:Java序列化代理模式
这是Java序列化官方提供的针对不可变类序列化的标准实现,完全适配你的场景,无需修改原有类的不可变属性定义,可直接复用kotlinx.serialization的全部能力:
- 无需实现
Externalizable接口,仅需为原有类实现java.io.Serializable,声明序列化版本号 - 新增私有静态内部类作为序列化代理,存储kotlinx序列化后的字节数据
- 通过Java序列化的内置钩子方法
writeReplace、readResolve完成代理和原实例的自动转换 - 可添加私有
readObject方法避免原类被直接反序列化,提高安全性
完整代码示例
import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToByteArray import kotlinx.serialization.decodeFromByteArray import kotlinx.serialization.json.Json import java.io.ObjectStreamException import java.io.Serializable @Serializable data class Foo( val a: A, // A类型已适配kotlinx序列化,不支持Java序列化 val z: Z // Z类型支持Java序列化 ) : Serializable { private companion object { // 自定义Java序列化版本号,避免版本兼容问题 private const val serialVersionUID = 1L // 私有序列化代理类,对外不可见 private class SerializationProxy( private val serializedBytes: ByteArray ) : Serializable { companion object { private const val serialVersionUID = 1L } // 反序列化代理时自动调用,返回实际的Foo实例 @Throws(ObjectStreamException::class) private fun readResolve(): Any { return Json.decodeFromByteArray<Foo>(serializedBytes) } } } // 序列化Foo实例时自动调用,替换实际序列化的对象为代理实例 @Throws(ObjectStreamException::class) private fun writeReplace(): Any { return SerializationProxy(Json.encodeToByteArray(this)) } // 禁止直接反序列化Foo类,只能通过代理完成反序列化 @Throws(ObjectStreamException::class) private fun readObject(stream: java.io.ObjectInputStream) { throw UnsupportedOperationException("该类仅支持通过序列化代理反序列化") } }
方案优势
- 完全适配Kotlin不可变类的设计,不需要修改原有类的属性定义
- 100%复用已有的kotlinx.serialization逻辑和自定义适配器,无需额外处理字段兼容
- 符合Java序列化官方规范,兼容性强,没有反射修改属性的安全隐患
- 你可以根据需求将示例中的
Json替换为kotlinx.serialization支持的任意序列化格式(如ProtoBuf、CBOR等),无需调整其他逻辑
不推荐的替代方案:反射实现Externalizable
如果有特殊场景必须实现Externalizable接口,可以通过Kotlin反射修改val字段的可访问性后赋值,但该方案会破坏类的不可变性,依赖反射实现性能和兼容性都较差,仅作为备选方案参考:
import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToByteArray import kotlinx.serialization.decodeFromByteArray import kotlinx.serialization.json.Json import java.io.Externalizable import java.io.ObjectInput import java.io.ObjectOutput import kotlin.reflect.full.memberProperties import kotlin.reflect.jvm.isAccessible @Serializable data class Foo( val a: A, val z: Z ) : Externalizable { companion object { private const val serialVersionUID = 1L } override fun writeExternal(out: ObjectOutput) { out.writeInt(0) // 写入版本号用于兼容 out.write(Json.encodeToByteArray(this)) } override fun readExternal(`in`: ObjectInput) { val version = `in`.readInt() val bytes = ByteArray(`in`.available()) `in`.read(bytes) val deserialized = Json.decodeFromByteArray<Foo>(bytes) // 反射给当前实例的val字段赋值 this::class.memberProperties.forEach { prop -> prop.isAccessible = true (prop as kotlin.reflect.KMutableProperty<*>).setter.call(this, prop.get(deserialized)) } } }
内容的提问来源于stack exchange,提问作者Eytan
相关产品推荐
相关产品推荐

