如果我写:
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)
即。 override val t
为特征t
中的u
设置T
的值?
答案 0 :(得分:12)
执行此操作的一种方法是使用u
或def
延迟对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
使用情况下对其进行评估并缓存结果答案 1 :(得分:10)
尝试使用早期初始化程序:
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)
参见例如here有关早期初始化的更多信息。
答案 2 :(得分:4)
所有scala声明式风格只是一种幻觉。 Scala构建于jvm之上,其工作方式与java类似。
Evetything是一个类,应该独立于它的用法(java不是c ++,支持增量构建及其优缺点)。每个特征都有自己的初始化代码,多特征类逐个运行各自的初始化代码。如果你使用一些仅在子类中声明的AnyRef,那么在初始化期间它的值将被设置为null。
我通过指定约定规则来保护自己:每个val应该是final或lazy(why using plain val in non-final classes)。所以我不关心初始化顺序,可能会假装我正在使用声明性语言。
此外,我正在使用选项-Xcheckinit
:将运行时检查添加到字段访问器。