Scala初始化顺序

时间:2019-08-07 08:10:42

标签: scala

在《 Scala编程》(第10章,“构成和继承”)一书中,有一个例子引起了一些误解。这是提取的部分:

s

由于某种原因,超类的初始化发生在scala example.scala === Element === UniformElement.s str null 初始化之前:

s

为什么在没有{{1}}的情况下替代方法有效(请参见代码中的注释行)?

2 个答案:

答案 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上)是这样的初始化顺序:

  1. 参数:在您的情况下,str是要定义的第一个值
  2. 父母:根据您的情况,Element
  3. 孩子:您的情况是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

如有需要,请随时澄清。