评估密封性状的扩展时出现意外行为

时间:2017-11-02 16:52:45

标签: scala scope

我有一个看起来像这样的特征:

trait MyTrait {
  val description: String
}

在另一个对象中,我有以下调用该特征:

object MyObject {
  sealed trait c extends MyTrait {
    val myItem: String
    val description: String = s"$myItem"
  }

  object c1 extends c {
    val myItem: String = "One"
  }

  object c2 extends c {
    val myItem: String = "Two"
  }
}

但是,在运行时,我c1.descriptionc2.description都是空对象。如果我重构代码看起来像这样,我得到预期的结果:

object MyObject {
  sealed trait c extends MyTrait {
    val myItem: String      
  }

  object c1 extends c {
    val myItem: String = "One"
    val description = s"$myItem"
  }

  object c2 extends c {
    val myItem: String = "Two"
    val description = s"$myItem"
  }
}

任何人都可以解释这种行为吗?

2 个答案:

答案 0 :(得分:1)

根据我的理解,所有val都按照外观的顺序进行评估,但是覆盖val由编译器转换,并且在评估的编译代码中,是""""不重写的。

所以:

object MyObject {
  sealed trait c extends MyTrait {
    val myItem: String = "This will print this string"
    val description: String = s"$myItem"
  }

  object c1 extends c {
  }

  object c2 extends c {
  }
}

作品,

object MyObject {
  sealed trait c extends MyTrait {
    val description: String = s"$myItem"
    val myItem: String = "This will not print this string"
  }

  object c1 extends c {
  }

  object c2 extends c {
  }
}

没有,正如你所注意到的那样,你的方式也没有。

最简单的方法是将描述写成懒惰的val。

作为一个更一般的规则,我通常会在特征中看到最终的val(禁止覆盖)或def,以允许任何类型的覆盖。在你的情况下,也许描述应该是最终的懒惰,而myItem是def?

答案 1 :(得分:0)

  1. val被初始化为构造函数的一部分。在此之前,该字段仍然存在,但具有默认值(null / 0 / false /等等,具体取决于类型)。

  2. 超类型c的构造函数在子类型(c1c2)的构造函数之前自然执行。