7

如果我写:

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

4

3 回答 3

12

一种方法是u使用defor来延迟评估,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使用时进行评估并缓存结果
于 2013-04-02T11:38:34.280 回答
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)

有关早期初始化的更多信息,请参见此处

于 2013-04-02T11:44:47.773 回答
4

所有 scala 声明式风格都只是一种错觉。Scala 是建立在 jvm 之上的,并且像 java 一样工作。

Evetything 是一个类,应该独立于它的使用(java 不是 c++ 并且支持增量构建,它的优点和缺点)。每个 trait 都有自己的初始化代码,multi-trait 类一个接一个地运行各自的初始化代码。如果您使用一些仅在子类中声明的 AnyRef,则其值将在初始化期间设置为 null。

我用指定的约定规则来保护自己:每个 val 都应该是 final 或惰性的(为什么在非 final 类中使用普通 val)。所以我不关心初始化顺序,可能会进一步假装我正在使用声明性语言。

我也在使用选项-Xcheckinit:向字段访问器添加运行时检查。

于 2013-04-02T13:13:10.317 回答