我有一个看起来像这样的特征:
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.description
和c2.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"
}
}
任何人都可以解释这种行为吗?
答案 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)
val
被初始化为构造函数的一部分。在此之前,该字段仍然存在,但具有默认值(null
/ 0
/ false
/等等,具体取决于类型)。
超类型c
的构造函数在子类型(c1
或c2
)的构造函数之前自然执行。