如何从子类型中的trait初始化值?

时间:2013-04-02 11:12:50

标签: scala traits

如果我写:

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的值?

3 个答案:

答案 0 :(得分:12)

执行此操作的一种方法是使用udef延迟对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:将运行时检查添加到字段访问器。