请查看以下代码。
trait MyTrait { val myVal : String }
class MyClass extends MyTrait { val myVal = "Value" }
class MyClass2(val myVal: String) extends MyTrait
为什么MyClass
和MyClass2
的初始化顺序不同?
MyClass
的构造函数将为
MyClass() {
MyTrait$class.$init$(this);
myVal = value
}
MyClass2
的构造函数将是
MyClass2(String myVal) { this.myVal = myVal; MyTrait$class.$init$(this) }
我认为初始化顺序应该是MyClass2
的构造函数,两种情况都是一样的。
答案 0 :(得分:23)
在Scala specification的第5.1节末尾,定义了以下内容:
模板评估。考虑模板 sc mt 1 mt n {stats} 。如果这是模板 特征(§5.3.3)然后是它 mixin-evaluation由一个评估组成 语句序列的使用 统计。如果这不是a的模板 特质,然后它的评价包括 以下步骤。
- 首先,评估超类构造函数sc(第5.1.1节)。
- 然后,模板线性化(第5.1.2节)中的所有基类向上 到模板的超类表示 通过sc进行混合评估。 Mixin评估反过来发生 发生的顺序 线性化。
- 最后评估语句序列统计信息。
但是,请注意,构造函数参数可以由跟随它的任何构造函数使用。因此,需要在它们之前进行初始化。这在第5.1.1节末尾明确说明:
构造函数的评估 调用 x.c targs。 。 。(argsn)由以下内容组成 步骤进行:
- 首先,评估前缀 x 。
- 然后,参数 args1 ,. 。 。 ,argsn 从左到右进行评估 右。
- 最后,正在构建的类通过评估来初始化 所引用的类的模板 C
这对您没有任何问题,但您最近执行的 {stats} 确实存在问题。 {stats} 最后执行的原因是它可能引用其祖先类和特征的属性,而祖先显然不知道它的后代。因此,在 {stats} 执行之前,需要完全初始化祖先。
当然, 可能需要提前初始化。第5.1.6节:早期定义涵盖了这一点。这是你写的方式:
class MyClass extends { val myVal = "Value" } with MyTrait