出于学习目的,我指的是“不耐烦的 Scala”一书。我找到了一些施工顺序问题的例子和早期定义的解决方案。
这本书试图通过以下课程展示问题:
class Creature {
val range: Int = 10
val env: Array[Int] = new Array[Int](range)
}
class Ant extends Animal {
override val range: Int = 2
}
这里,根据构造顺序问题,在创建类 Ant
的实例时,会调用超类 Creature
构造函数,并使用默认的 Int 值零初始化 env 数组。
为了解决这个问题,他们提到了三种可能的解决方案:
很容易从逻辑上理解point#2 和point#3。
但是我们如何通过在子类中将 range
字段声明为 final
来解决这个问题?
我尝试了以下解决方案并且奏效了。但我无法在逻辑上将子类中的 field
重写为 final
如何解决这个问题?
package ed2 {
class Creature {
val range: Int = 10
val env: Array[Int] = new Array[Int](range)
}
class Ant extends Creature {
final override val range = 2
}
}
注意:这里要注意的一个重点是,我们能够在推迟子类 final override val range = 2
中最终覆盖字段的类型声明时产生这种行为。如果我们在子类中声明字段类型,此代码段返回零。
编译器如何处理子类的 final
字段?是否优先考虑最终成员进行初始化?
答案 0 :(得分:2)
您的书是正确的,但不完全正确。 Scala 中有一种表达式,称为 Constant Value。它在任何使用它的地方都被内联:
<块引用>final 修饰符必须存在并且不能给出类型注释。对常量值 x 的引用本身被视为常量表达式;在生成的代码中,它们被定义的右侧 e 替换。
简而言之,
然后它们被内联在类定义中,因此在您的 Ant 类中它是 2。
在其他情况下,如果您提供像 final override val range: Int = 2
这样的类型定义,则它不被视为常量值且不内联。因此它按照继承的规则顺序进行初始化。