Scala无法初始化val

Scala无法初始化val,scala,nullpointerexception,Scala,Nullpointerexception,我在下面的Scala程序中发现了一些奇怪的地方(很抱歉包含了所有的代码,但您将看到我为什么添加了所有代码): 如果在某个some code…之后定义了VALID\u选项,则在parseOption中将其计算为null。我看不出有什么好的理由。为了清晰起见,我截断了代码,但是如果需要更多的代码,我很乐意添加它 编辑:我仔细查看了一下,以下是我的发现 扩展App时,val未使用此代码初始化 object Test extends App { printTest() def print

我在下面的Scala程序中发现了一些奇怪的地方(很抱歉包含了所有的代码,但您将看到我为什么添加了所有代码):

如果在某个
some code…
之后定义了
VALID\u选项
,则在
parseOption
中将其计算为null。我看不出有什么好的理由。为了清晰起见,我截断了代码,但是如果需要更多的代码,我很乐意添加它

编辑:我仔细查看了一下,以下是我的发现

扩展
App
时,
val
未使用此代码初始化

object Test extends App {
    printTest()
    def printTest = println(test)
    val test = "test"
}
使用常规的main方法,它可以很好地工作:

object Test {
    def main(args: Array[String]): Unit = {
      printTest
    }
    def printTest = println(test)
    val test = "test"
}

构造函数体,这也适用于单例对象,严格地从上到下进行计算。不幸的是,这是Scala中的一个常见陷阱,因为如果在构造函数的其他位置引用了
val
s,则在定义它们的地方,这就变得很重要了

object Foo {
  val rab = useBar    // oops, involuntarily referring to uninitialized val 
  val bar = "bar"

  def useBar: String = bar.reverse        
}

Foo   // NPE

当然,在更好的情况下,Scala编译器要么不允许上述代码,要么重新排序初始化,要么至少警告您。但它没有…

我监督过你使用
扩展应用程序。不幸的是,这是Scala的另一个陷阱:

object Foo extends App {
  val bar = "bar"
}

Foo.bar            // null!
Foo.main(Array())
Foo.bar            // now initialized
App
trait将对象的初始化延迟到调用
main
方法,因此在调用
main
方法之前,所有
val
都是
null


总之,
App
trait和
val
s不能很好地结合。
我已经多次落入这个陷阱。如果您使用
App
,请避免使用
val
s,如果您必须使用全局状态,请使用
lazy val
s。

如果您正在寻找比Suma和0更具体的原因,则可能重复“必须是初始化顺序”,您需要发布一个足够详细的简单示例,以便可以运行它来重现问题。这不是重复的,因为我还有一个额外的因素:我正在扩展
应用程序
哦,对了。我以前在什么地方见过这个。如果没有重复的问题,我会很惊讶。您能否至少添加调用
parseOption()
的方式和时间?@DanGetz我试图添加相关的最小代码。嗯,等等,它实际上不太相关,
一些代码2
高于
val
的第一次使用,那么为什么我在注释下声明它时它仍然计算为null?如果当您将
val
更改为
lazy val
时NPE消失,我保证您的问题是初始化顺序,而且这可能不是第一眼看到的。这肯定与我正在扩展
App
这一事实有关。当然,这与初始化的顺序有关,但在常规的main方法(已测试)中不会发生
object Foo extends App {
  val bar = "bar"
}

Foo.bar            // null!
Foo.main(Array())
Foo.bar            // now initialized