Scala中object与val初始化顺序异常问题咨询
Scala单例对象内部成员初始化顺序导致的null问题分析
先看问题代码:
class MyClass() class MyOtherClass(c: MyClass) object Foo { val myClass = new MyClass() object otherClass extends MyOtherClass(myClass) val otherClasses = Seq(otherClass) } object Main { def main(args: Array[String]): Unit = { println(Foo.otherClass) println(Foo.otherClasses) } }
运行输出:
Foo$otherClass$@5410511c List(null)
原因解析
这不是Scala的Bug,而是单例对象初始化规则导致的边缘情况,你的错误假设在于认为嵌套单例对象和普通val的初始化顺序完全一致。
Scala顶层单例对象的初始化逻辑是:
- 优先初始化所有非
lazy的普通val/var成员 - 嵌套的单例对象(比如这里的
Foo.otherClass)属于延迟初始化,只有当它被主动显式引用时才会完成初始化流程。
在你的代码里,otherClasses作为非lazy的val,会在Foo初始化阶段被优先处理。此时它引用otherClass,但otherClass的单例还没完成初始化,JVM会暂时用null占位。等otherClasses初始化完成后,main里第一次主动调用Foo.otherClass才会触发这个嵌套单例的真正初始化,所以第一次打印能拿到正常实例地址,但otherClasses里已经存了初始化时的null引用。
可行解决方案
除了你已经用到的把otherClasses设为lazy,还有两种更贴合需求的方式:
- 将嵌套单例改为普通val:如果
otherClass不需要作为单例使用,直接改成普通实例即可,这样会按从上到下的顺序初始化,otherClasses能拿到正确引用:object Foo { val myClass = new MyClass() val otherClass = new MyOtherClass(myClass) val otherClasses = Seq(otherClass) } - 显式触发嵌套单例初始化:如果必须保留
otherClass作为单例,可以在otherClasses初始化时主动触发它的初始化:object Foo { val myClass = new MyClass() object otherClass extends MyOtherClass(myClass) val otherClasses = { otherClass // 主动触发生成实例 Seq(otherClass) } }
内容的提问来源于stack exchange,提问作者bobismijnnaam
相关产品推荐
相关产品推荐

