Scala 继承 - 如何将字段声明为最终解决构造顺序问题

时间:2021-05-22 18:51:10

标签: scala inheritance

出于学习目的,我指的是“不耐烦的 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 数组。

为了解决这个问题,他们提到了三种可能的解决方案:

  1. 将 val 声明为 final
  2. 在超类中声明 val 为惰性
  3. 使用早期定义语法

很容易从逻辑上理解point#2point#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 字段?是否优先考虑最终成员进行初始化?

1 个答案:

答案 0 :(得分:2)

您的书是正确的,但不完全正确。 Scala 中有一种表达式,称为 Constant Value。它在任何使用它的地方都被内联:

<块引用>

final 修饰符必须存在并且不能给出类型注释。对常量值 x 的引用本身被视为常量表达式;在生成的代码中,它们被定义的右侧 e 替换。

简而言之,

  1. 需要final关键字
  2. 它不需要类型注释

然后它们被内联在类定义中,因此在您的 Ant 类中它是 2。

在其他情况下,如果您提供像 final override val range: Int = 2 这样的类型定义,则它不被视为常量值且不内联。因此它按照继承的规则顺序进行初始化。

相关问题