覆盖"val"时出现意外结果



在Scala 2.10.4中,给定以下类:

scala> class Foo { 
     |   val x = true
     |   val f = if (x) 100 else 200
     | }
defined class Foo

以下两个例子对我来说很有意义:

scala> new Foo {}.f
 res0: Int = 100
scala> new Foo { override val x = false}.f
res1: Int = 200

但是,为什么这个调用不返回100

scala> new Foo { override val x = true }.f
res2: Int = 200

因为vals不会多次初始化,所以在Foo的初始化过程中,x实际上是null(或默认Booleanfalse),然后在示例中扩展Foo的匿名类中初始化。

我们可以用AnyRef:更容易地测试它

class Foo { 
    val x = ""
    val f = if (x == null) "x is null" else "not null"
}
scala> new Foo { override val x = "a" }.f
res10: String = x is null
scala> new Foo {}.f
res11: String = not null

Scala常见问题解答中有一个完整的解释。摘录:

自然地,当一个val被重写时,它不会被多次初始化。因此,尽管上面例子中的x2似乎在每一点都被定义了,但事实并非如此:在构造超类的过程中,重写的val将看起来为null,抽象的val也是如此。

避免这种情况的一个简单方法是使用惰性val或def,如果被引用的val可能被重写的话。

此外,您可以使用-Xcheckinit编译器标志来警告您可能出现的初始化错误。