You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,还有两种更贴合需求的方式:

  1. 将嵌套单例改为普通val:如果otherClass不需要作为单例使用,直接改成普通实例即可,这样会按从上到下的顺序初始化,otherClasses能拿到正确引用:
    object Foo {
      val myClass = new MyClass()
      val otherClass = new MyOtherClass(myClass)
      val otherClasses = Seq(otherClass)
    }
    
  2. 显式触发嵌套单例初始化:如果必须保留otherClass作为单例,可以在otherClasses初始化时主动触发它的初始化:
    object Foo {
      val myClass = new MyClass()
      object otherClass extends MyOtherClass(myClass)
      val otherClasses = {
        otherClass // 主动触发生成实例
        Seq(otherClass)
      }
    }
    

内容的提问来源于stack exchange,提问作者bobismijnnaam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 19:42:38