为何Scala单例对象Works.factory可序列化而Broken.factory不可?
为什么
Works.factory可序列化,而Broken.factory不行? 这个问题的核心在于Scala单例对象、特质(trait)与Java序列化机制的交互细节,咱们一步步拆解:
1. 先看Works的正常情况
Works是一个顶层Scala单例对象,Scala会自动让所有顶层单例对象继承scala.Serializable,而且对单例对象的序列化有特殊优化:序列化时只保存一个标识,反序列化时直接返回全局唯一的单例实例,不会重新创建。
而Works里的factory是一个匿名Serializable类实例,它引用了外部的i方法。这个匿名类会持有对Works实例的引用——因为Works本身是可序列化的,所以整个factory实例在序列化时,JVM可以正常处理这个外部引用,自然就能顺利序列化。
2. 再看Broken的问题所在
Broken是继承自trait A的单例对象,这里的坑点出在trait A的定义上:
- 虽然
Broken作为单例对象默认是Serializable的,但trait A本身没有继承Serializable。 - 在
trait A中定义的factory匿名类,它引用的i是trait A中的方法,所以这个匿名类会持有一个静态类型为A的引用,指向Broken实例。 - Java序列化机制是基于静态类型检查可序列化性的:当序列化
Broken.factory时,JVM看到它持有一个A类型的引用,而A没有标记为Serializable,不管实际实例Broken是不是可序列化,都会直接抛出NotSerializableException。
验证与修复
如果把trait A修改为继承Serializable,问题就解决了:
trait A extends Serializable { def i = ??? val factory = new Serializable { def get = i } }
这时Broken.factory就能正常序列化——因为此时匿名类持有的A类型引用本身是可序列化的,JVM可以正常处理。
内容的提问来源于stack exchange,提问作者Kaarel Nummert
相关产品推荐
相关产品推荐

