- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在阅读“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 最终是一个空的 Array[Int]:
[..] 3. The Animal constructor, in order to initialize the env array, calls the range() getter.
That method is overridden to yield the (as yet uninitialized) range field of the Ant class.
The range method returns 0. (That is the initial value of all integer fields when an object is allocated.)
env is set to an array of length 0.
- The Ant constructor continues, setting its range field to 2.[..]
我不明白第 4 步,因此接下来的步骤也不清楚。 range() 方法被 2 覆盖了,那么为什么它不在第 4 步中设置范围呢?
Doe 是这样工作的吗?当 val 被覆盖时,它就会被取消初始化,并且所有包含这个被覆盖的 gals 的 val 也会被修改。这是对的吗?如果是,为什么 def 会出现不同的行为,如 here 所述.为什么 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)
然后使用 -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
.此外,还有完全独立的def
也称为range
的s在脱糖代码中:这些是为 val
自动生成的 setter/getter
第一个变量确实是在Animal
中初始化的并设置为 10
:
anon$1$Animal.this.range = 10;
不过,这没关系,因为env
使用 setter/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)
在这里,同样的事情发生了:
_Animal_range
分配了默认值 0_Ant_range
分配了默认值 0Animal
基类开始初始化_Animal_range
初始化为值 10env
, setter/getter range
被调用。它在 Ant
中被覆盖-类,并返回 _Ant_range
, 仍然是 0env
设置为空数组Animal
基类完成初始化Ant
开始初始化_Ant_range
至 2
.这就是为什么两个代码片段都打印出 2
的原因和 0
.
希望对您有所帮助。
关于scala:构造顺序和早期定义(继承),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49599115/
我正在为从 API 级别 8 到 14 的 android 开发一个应用程序。我正在尝试在早期版本中获得与 android 4(请参阅联系人应用程序)相同的快速滚动行为(右侧固定的时尚滚动条)边)。有
早期(编译期)优化 jvm的编译器可以分为三个编译器: 前端编译器:把*.java转变为*.class的过程。如sun的javac、eclipse jdt中的增量式编译器(ecj)
苹果终于推出了所谓的auto-renewable subscriptions昨天。由于我在应用内购买方面的经验很少(仅限沙盒),所以我不确定我在这里是否一切顺利。似乎需要对收据进行服务器端验证。找出订
已结束。此问题不符合 Stack Overflow guidelines .它目前不接受答案。 要求代码的问题必须表明对正在解决的问题的最低理解。包括尝试的解决方案、它们为什么不起作用以及预期结果。另
在 Wagner 的“Effective C#”第 23 项中,他解释说 interface methods are not virtual...they are a declaration of a
我最近遵循了本指南 Installing a Git Server using Apache (WebDAV) on Ubuntu Server 12.04使用 Apache (WebDAV) 设置本
这是我之前的问题 jQuery UI hiding not taking effect for early DOM elements 的后续问题。我几乎刚刚编辑了那个,但不想使 the accepte
我正在尝试替换 ZonedDateTime.toInstant方法,因为它仅从 API 26 for Android 开始可用。 但我的应用程序应该支持 API 19。 我想将 ZonedDateTi
我的电脑正确配置了 SSH,我在尝试克隆存储库时遇到了这个错误: 我运行这个命令来克隆存储库 git clone ssh://git-codecommit.us-west-2.amazonaws.co
我是一名优秀的程序员,十分优秀!