如果我写:
trait T {
val t = 3
val u = 1::t::Nil
}
class U extends T {
override val t = 2
}
(new U).u
它表明了这一点。
List(1, 0)
我应该如何更改上面的代码以使其显示以下内容:
List(1, 2)
即在特征中设置foroverride val t
的值?t
u
T
一种方法是u
使用def
or来延迟评估,lazy val
如下所示:
trait T {
def t = 3
def u = 1::t::Nil
}
class U extends T {
override def t = 2
}
(new U).u
或者
trait T {
val t = 3
lazy val u = 1::t::Nil
}
class U extends T {
override val t = 2
}
(new U).u
区别如下:
val
在初始化期间使表达式求值def
每次u
使用时都对表达式求值lazy val
使其在首次u
使用时进行评估并缓存结果尝试使用早期初始化程序:
scala> trait T {
| val t = 3
| val u = 1::t::Nil
| }
defined trait T
scala> class U extends {
| override val t = 2;
| } with T
defined class U
scala> (new U).u
res1: List[Int] = List(1, 2)
有关早期初始化的更多信息,请参见此处。
所有 scala 声明式风格都只是一种错觉。Scala 是建立在 jvm 之上的,并且像 java 一样工作。
Evetything 是一个类,应该独立于它的使用(java 不是 c++ 并且支持增量构建,它的优点和缺点)。每个 trait 都有自己的初始化代码,multi-trait 类一个接一个地运行各自的初始化代码。如果您使用一些仅在子类中声明的 AnyRef,则其值将在初始化期间设置为 null。
我用指定的约定规则来保护自己:每个 val 都应该是 final 或惰性的(为什么在非 final 类中使用普通 val)。所以我不关心初始化顺序,可能会进一步假装我正在使用声明性语言。
我也在使用选项-Xcheckinit
:向字段访问器添加运行时检查。