【问题标题】:How to initialize the value from trait in subtype?如何从子类型中的特征初始化值?
【发布时间】:2016-03-31 23:28:28
【问题描述】:

如果我写:

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 为 trait u 设置 t 的值 T?

【问题讨论】:

  • 你可以定义为lazy val

标签: scala traits


【解决方案1】:

一种方法是使用deflazy val 延迟对u 的评估,如下所示:

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 在初始化期间使表达式求值
  • 每次使用 u 时,def 都会对表达式进行评估
  • lazy val 在第一次使用 u 时对其进行评估并缓存结果

【讨论】:

    【解决方案2】:

    尝试使用早期初始化器:

    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 了解有关早期初始化的更多信息。

    【讨论】:

    • 虽然正确,但出于可读性原因,我更喜欢将缓存方法与惰性 val 一起使用。如果我需要向我的 U 类添加代码,我不能把它放在括号中,我需要创建另一个组,比如 class U extends { override val t=2} with T { ... my code here ...}
    • 这种方法的问题是每次访问都是同步的,以确保lazy val实际上已经准备好了。它会减慢应用程序的速度,不应在关键路径上盲目使用。
    【解决方案3】:

    所有 scala 声明式风格都只是一种错觉。 Scala 建立在 jvm 之上,工作方式与 java 类似。

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

    我用指定的约定规则来保护自己:每个 val 都应该是 final 或 lazy (why using plain val in non-final classes) 。所以我不关心初始化顺序,可能会进一步假装我正在使用声明性语言。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-06-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-04
      • 2022-08-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多