我正在阅读“不耐烦的 Scala”,在 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
}
Run Code Online (Sandbox Code Playgroud)
作者解释了为什么 env 最终是一个空的 Array[Int]:
[..] 3. 为了初始化 env 数组,Animal 构造函数调用了 range() 获取器。
该方法被重写以生成 Ant 类的(尚未初始化的)范围字段。
range 方法返回 0。(这是分配对象时所有整数字段的初始值。)
env 设置为长度为 0 的数组。
- Ant 构造函数继续,将其范围字段设置为 2.[..]
我不明白第 4 步,因此接下来的步骤也不清楚。range() 方法被 2 覆盖,那么为什么它不在第 4 步中设置范围呢?
是这样工作的吗?当 val 被覆盖时,它会被取消初始化,并且所有包含这个被覆盖的 gal 的 val 也会被修改。这是正确的吗?如果是的话,为什么会有不同的行为与高清所概述这里。为什么在构造函数调用之前定义了 def 而在之后定义了 val?
在您发表评论后,我决定实际看看书中到底写了什么。看完解释后,我决定我不能更清楚地表达它了。所以相反,我建议看一下完全脱糖的代码,这本小书里没有。
将其另存为 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)
Run Code Online (Sandbox Code Playgroud)
然后使用-print
-option运行它:
> scala -nc -print yourScript.scala
Run Code Online (Sandbox Code Playgroud)
你应该看到这样的东西:
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;
()
}
}
Run Code Online (Sandbox Code Playgroud)
这是编译器在编译的后期阶段看到的脱糖代码。这有点难以阅读,但重要的是这些声明:
// 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;
Run Code Online (Sandbox Code Playgroud)
以及初始化器中的语句Animal
:
anon$1$Animal.this.env = new Array[Int](anon$1$Animal.this.range())
Run Code Online (Sandbox Code Playgroud)
您在这里可以看到实际上有两个不同的变量range
:一个是Animal.this.range
,另一个是Ant.this.range
。此外,在脱糖代码def
中也调用range
了完全独立的s :这些是为val
s自动生成的 getter 。
第一个变量确实被初始化Animal
并设置为10
:
anon$1$Animal.this.range = 10;
Run Code Online (Sandbox Code Playgroud)
然而,这并不重要,因为env
是使用 getter 初始化的range()
,它被覆盖为 return 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)
Run Code Online (Sandbox Code Playgroud)
在这里,同样发生:
_Animal_range
以默认值 0 分配_Ant_range
以默认值 0 分配Animal
基类开始初始化_Animal_range
用值 10 初始化env
,range
调用getter 。它在Ant
-class 中被覆盖,并返回_Ant_range
,它仍然是 0env
设置为空数组Animal
基类完成初始化Ant
开始初始化_Ant_range
为2
.这就是为什么两个代码片段都打印2
和0
.
希望有帮助。