在《 Scala编程》(第10章,“构成和继承”)一书中,有一个例子引起了一些误解。这是提取的部分:
s
由于某种原因,超类的初始化发生在scala example.scala
=== Element
=== UniformElement.s str
null
初始化之前:
s
为什么在没有{{1}}的情况下替代方法有效(请参见代码中的注释行)?
答案 0 :(得分:4)
问题是字段值null
直到构造函数完成,而超级构造函数间接引用由子构造函数初始化的值s
,但子构造函数尚未完成。情况看起来像这样
class UniformElement {
def <init>(str: String) = {
super.<init>()
s = str
}
}
我们可以在其中查看是否将super.<init>()
替换为
val someProperty: String = {
println("=== Element")
contents(0)
}
它先执行
s = str
Initialisation order问题通常可以通过将急切的val s
变为惰性来解决,
class UniformElement(str: String) extends Element {
lazy val s = str
println("=== UniformElement.s " + s)
def contents = Array(s)
}
现在输出
=== Element
=== UniformElement.s str
str
答案 1 :(得分:3)
感谢您提出有趣的问题!我的猜测(花了一些时间在Scastie上)是这样的初始化顺序:
str
是要定义的第一个值Element
UniformElement
因此,如果我尝试将其放在单个班级中,它将像这样:
class UniformElement{
// Argument init
val str = "str"
// Super constructor
def contents: Array[String]
val someProperty: String = {
println("=== Element")
contents(0)
}
// Child constructor
val s = str
println("=== UniformElement.s " + s)
def contents = Array(s) // error
//def contents = Array(str) // ok
}
诀窍在于,要初始化someProperty
,scala需要评估contents(0)
并找到contents
的定义。但是,当找到定义时,s
尚未定义(而str
已定义)。
最后的“运行时”过程:
class UniformElement{
// Argument init
val str = "str"
// Super constructor with contents replaced by definition
val someProperty: String = {
println("=== Element")
Array(s)(0) // error : s doesn't exists !
// Array(str)(0) // ok : str exists
}
// Child constructor
val s = str
println("=== UniformElement.s " + s)
def contents = Array(s) // error
//def contents = Array(str) // ok
}
要说服自己,您可以尝试:
println(e.someProperty) // null => s wasn't defined
println(e.contents(0)) // str => s is now defined
如有需要,请随时澄清。