Var,Val 和 Def
由于 val
在语义上是静态的,因此无论它们出现在代码中,它们都会就地初始化。当在抽象类和特征中使用时,这会产生令人惊讶和不良行为。
例如,假设我们想制作一个名为 PlusOne
的特征,它定义了一个包装的 Int
的增量操作。由于 Int
s 是不可变的,所以在初始化时已知值加 1,之后永远不会改变,因此在语义上它是一个 val
。但是,以这种方式定义它将产生意想不到的结果。
trait PlusOne {
val i:Int
val incr = i + 1
}
class IntWrapper(val i: Int) extends PlusOne
无论你使用什么价值 i
,在返回的对象上调用 .incr
将始终返回 1.这是因为 val incr
在特征中初始化,在扩展类之前,并且此时 i
仅具有默认值 0
。 (在其他情况下,它可能会填充 Nil
,null
或类似的默认值。)
因此,一般规则是避免在依赖于抽象字段的任何值上使用 val
。相反,使用 lazy val
,它在需要时不进行评估,或使用 def
,每次调用时都会进行评估。但请注意,如果在初始化完成之前 lazy val
强制要求 lazy val
进行评估,则会发生相同的错误。