Scala无法初始化val

时间:2015-05-24 18:03:12

标签: scala nullpointerexception

我在下面的Scala程序中发现了一种奇怪的现象(很抱歉包含所有代码,但是你会明白我为什么要添加它们):

object md2html extends App {
    private val DEFAULT_THEME = Themes.AMAZON_LIGHT

    private val VALID_OPTIONS = Set("editorTheme", "logo", "style")
    try {
        // some code 1
    } catch {
        case t: Throwable => t.printStackTrace(); exitWithError(t.getMessage)
    }

    // some code 2 (method definitions only)

    private def parseOption(key: String, value: String) = {
        println(key + " " + VALID_OPTIONS)
        if (! Set("theme","editorTheme", "logo", "style").contains(key)) exitWithError(s"$key is not a valid option")   
        if (key == "theme") Themes(value).toMap else Map(key.drop(2) -> value)
    }

    // some code 3 (method definitions only)
}

如果在VALID_OPTIONS之一之后定义了some code...,则会在parseOption中将其评估为空。我认为没有充分的理由。为了清晰起见,我截断了代码,但如果需要更多代码,我很乐意添加它。

编辑:我看了一下,这就是我找到的。

扩展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"
}

2 个答案:

答案 0 :(得分:10)

我曾监督你使用extends App。不幸的是,这是斯卡拉的另一个陷阱:

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

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

App特征将对象的初始化推迟到main方法的调用,因此所有val都是null,直到main方法具有被叫了。

总之,App特质和val混合不好。我已多次陷入陷阱。如果您使用App,请避免使用val,如果必须使用全局状态,请改用lazy val

答案 1 :(得分:1)

构造函数体,这也适用于单例对象,从上到下严格评估。不幸的是,这是Scala中常见的陷阱,因为如果在构造函数的其他位置引用了val,它就变得相关。

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

  def useBar: String = bar.reverse        
}

Foo   // NPE

当然,在一个更好的世界中,Scala编译器要么不允许上面的代码,重新排序初始化,要么至少警告你。但它没有......