Serialization 无法序列化Kotlin中的对象单例

Serialization 无法序列化Kotlin中的对象单例,serialization,kotlin,Serialization,Kotlin,在Kotlin中,密封类的一个常见用途是将包含一些数据(使用数据类)和其他单例(使用对象)的案例组合在一起,如下所示: sealed class Location { object Unknown : Location() data class Known(val lat: Float, val lon: Float) : Location() } 我们正在使用的系统要求模型实现可序列化。令我惊讶的是,这不适用于对象单例,正如您在本演示中看到的: 其输出如下: Success

在Kotlin中,密封类的一个常见用途是将包含一些数据(使用
数据类
)和其他单例(使用
对象
)的案例组合在一起,如下所示:

sealed class Location {
    object Unknown : Location()
    data class Known(val lat: Float, val lon: Float) : Location()
}
我们正在使用的系统要求模型实现
可序列化
。令我惊讶的是,这不适用于
对象
单例,正如您在本演示中看到的:

其输出如下:

Success: Known(lat=37.563934, lon=-116.85123) and Known(lat=37.563934, lon=-116.85123) are equal
Failure: sample.Location$Unknown@7c3df479 and sample.Location$Unknown@452b3a41 are not equal
实例id不同。我的猜测是JVM使用“artifital”方法对其进行反序列化。无论是反射还是其他合成方式

我怎样才能做到这一点呢?

我找到了一个“最好”的解决方案。使用
readResolve()
的隐藏JVM API:

代码如下:

生成以下输出:

Success: Known(lat=37.563934, lon=-116.85123) and Known(lat=37.563934, lon=-116.85123) are equal
Success: sample.Location$Unknown@452b3a41 and sample.Location$Unknown@452b3a41 are equal
此函数在从流加载对象后调用,并允许返回不同的对象,而不是从内存加载的对象

这意味着,即使我们的
对象
有一个状态,也可以安全地使用它(虽然它不应该这样做,而且应该更高效地使用内存)

这里有一张关于这个的票:

看起来它可能是另一张票的一部分,添加了
@JVMSerializable
注释:

旧答案:

我找到的最佳解决方案是使singleton对象覆盖默认的
equals
hashCode
toString
,使其功能完全相同:

sealed class Location: Serializable {
    object Unknown : Location() {
        override fun equals(other: Any?) = other is Unknown
        override fun hashCode() = toString().hashCode()
        override fun toString(): String = "Location.Unknown"
    }
    data class Known(val lat: Float, val lon: Float) : Location()
}
以下是演示:

输出为:

Success: Known(lat=37.563934, lon=-116.85123) and Known(lat=37.563934, lon=-116.85123) are equal
Success: Location.Unknown and Location.Unknown are equal
如果内存不是一个极端的问题,这是一个可能的解决方案,因为它将为每个反序列化对象创建一个对象。尽管如此,该对象的占用空间非常小,这在大多数情况下不应该是一个问题,但需要注意

如果Kotlin可以使用枚举类实现
object
,因为它们在JVM中的序列化方式不同,那么这不会是一个问题。但是,它们不能扩展类(只扩展接口),因此它不适用于此实例

所有这些都表明,甲骨文计划最终放弃:

同时,我们也要面对这个问题


注意:只要对象是不可变的,这就可以工作。如果我们允许更改状态,当我们开始在每个反序列化对象上有不同的状态时,所有的地狱都会打开。

对我来说,这似乎是Kotlin语言中的一个错误,因为他们应该禁止序列化对象,或者让它们实现适当的
equals/hashcode
函数你在下面的回答中显示了什么。我还建议+1这个问题,让他们知道有多少人有同样的问题:
Success: Known(lat=37.563934, lon=-116.85123) and Known(lat=37.563934, lon=-116.85123) are equal
Success: Location.Unknown and Location.Unknown are equal