scala:施工顺序和早期定义(继承)

时间:2018-04-01 14:33:42

标签: scala

我正在阅读" Scala for the Impatient"在8.10中有一个例子:

class Animal {
  val range: Int = 10
  val env: Array[Int] = new Array[Int](range)

}

class Ant extends Animal {
  override val range: Int = 2
}

作者解释了为什么env最终成为一个空数组[Int]:

  

[..]   3.为了初始化env数组,Animal构造函数调用range()getter。

     
      
  1. 重写该方法以产生Ant类的(尚未初始化的)范围字段。

  2.   
  3. range方法返回0.(这是分配对象时所有整数字段的初始值。)

  4.   
  5. 将env设置为长度为0的数组。

  6.   
  7. Ant构造函数继续,将其范围字段设置为2。[..]
  8.   

我不理解第4步,因此接下来的步骤也不清楚。 range()方法被2覆盖,那么为什么它不在第4步中设置范围?

是的,Doe是这样的吗?当覆盖val时,它会被取消初始化,并且包含这个被覆盖的gals的所有val也会被修改。这是对的吗?如果是,为什么def与here概述的行为不同。为什么在构造函数调用和val之后定义def?

2 个答案:

答案 0 :(得分:2)

在你的评论之后,我决定真正看看书中究竟写了什么。在阅读了解释后,我决定不能更清楚地表达它。所以相反,我建议看看完全脱落的代码,这本书在短篇小说中没有位置。

将其另存为Scala脚本:

class Animal {
  val range: Int = 10
  val env: Array[Int] = new Array[Int](range)
}

class Ant extends Animal {
  override val range: Int = 2
}

val ant = new Ant
println(ant.range)
println(ant.env.size)

然后使用-print - 选项运行它:

> scala -nc -print yourScript.scala
你应该看到这样的事情:

class anon$1$Animal extends Object {
  private[this] val range: Int = _;
  <stable> <accessor> def range(): Int = anon$1$Animal.this.range;
  private[this] val env: Array[Int] = _;
  <stable> <accessor> def env(): Array[Int] = anon$1$Animal.this.env;
  <synthetic> <paramaccessor> <artifact> protected val $outer: <$anon: Object> = _;
  <synthetic> <stable> <artifact> def $outer(): <$anon: Object> = anon$1$Animal.this.$outer;
  def <init>($outer: <$anon: Object>): <$anon: Object> = {
    if ($outer.eq(null))
      throw null
    else
      anon$1$Animal.this.$outer = $outer;
    anon$1$Animal.super.<init>();
    anon$1$Animal.this.range = 10;
    anon$1$Animal.this.env = new Array[Int](anon$1$Animal.this.range());
    ()
  }
};
class anon$1$Ant extends <$anon: Object> {
  private[this] val range: Int = _;
  override <stable> <accessor> def range(): Int = anon$1$Ant.this.range;
  <synthetic> <stable> <artifact> def $outer(): <$anon: Object> = anon$1$Ant.this.$outer;
  def <init>($outer: <$anon: Object>): <$anon: anon$1$Animal> = {
    anon$1$Ant.super.<init>($outer);
    anon$1$Ant.this.range = 2;
    ()
  }
}

这是令人厌恶的代码,因为编译器在编译的后期阶段会看到它。它有点难以阅读,但重要的是这些声明:

  // in Animal:
  private[this] val range: Int = _;
  <stable> <accessor> def range(): Int = anon$1$Animal.this.range;

  // in Ant:
  private[this] val range: Int = _;
  override <stable> <accessor> def range(): Int = 
    anon$1$Ant.this.range;

以及Animal的初始化程序中的语句:

  anon$1$Animal.this.env = new Array[Int](anon$1$Animal.this.range())

您可以在此处看到,实际上有两个不同的变量range:一个是Animal.this.range,另一个是Ant.this.range。此外,在desugared代码中还有完全独立的def 称为range:这些是为val s自动生成的getter。 / p>

第一个变量确实在Animal中初始化并设置为10

    anon$1$Animal.this.range = 10;

但是,这并不重要,因为env是使用getter range()进行初始化的,后者会被覆盖以返回Ant.this.range。变量Ant.this.range的值已分配2一次,但 Animal的初始化程序后已完成。在Animal初始化期间,变量Ant.this.range保留默认值0,因此反直觉结果。

如果您稍微简化了这些代码,您将获得一个可编辑且可读的示例,其行为方式相同:

class Animal {
  private[this] var _Animal_range: Int = 0
  def range: Int = _Animal_range

  _Animal_range = 10
  val env: Array[Int] = new Array[Int](range)
}

class Ant extends Animal {
  private[this] var _Ant_range: Int = 0
  override def range: Int = _Ant_range

  _Ant_range = 2
}

val ant = new Ant
println(ant.range)
println(ant.env.size)

在这里,同样的事情发生了:

  1. _Animal_range分配了默认值0
  2. _Ant_range分配了默认值0
  3. Animal基类开始初始化
  4. _Animal_range初始化为值10
  5. 要初始化env,请调用getter range。它在Ant - 类中被覆盖,并返回_Ant_range,它仍为0
  6. env设置为空数组
  7. Animal基类完成初始化
  8. Ant开始初始化
  9. 现在才将_Ant_range设置为2
  10. 这就是为什么两个代码段都打印20

    希望有所帮助。

答案 1 :(得分:1)

在被要求时会调用

def,但是val被保留在内存中,所以在这种情况下,val版本仍然设置为零,因为它尚未初始化,def版本被调用,因此永远不会给出误导性的价值。

这只是因为这里的val被重载而没有及时初始化,因为必须从继承层次结构的顶部开始初始化类并进行构建。如果envdef你也可以,因为在调用之前它不会被创建,到那时val都将被初始化。当然,这样每次调用env时都会得到不同的列表,因此您可能更喜欢使用lazy val,它在第一次调用时初始化,但后来保持不变并保存在内存中