簡體   English   中英

Scala初始化順序

[英]Scala initialization order

在《 Scala編程》(第10章“合成與繼承”)一書中,有一個例子引起了一些誤解。 這是提取的部分:

abstract class Element {
  def contents: Array[String]
  val someProperty: String = {
    println("=== Element")
    contents(0)
  }
}

class UniformElement(
  str: String
) extends Element {
  val s = str
  println("=== UniformElement.s " + s)
  def contents = Array(s) // error
  //def contents = Array(str) // ok
}
val e = new UniformElement("str")
println(e.someProperty)

由於某種原因,超類s初始化發生在s初始化之前:

scala example.scala
=== Element
=== UniformElement.s str
null

為什么在沒有s情況下替代方法有效(請參見代碼中的注釋行)?

問題是字段值在構造函數完成之前為null ,並且超級構造函數間接引用由子構造函數初始化的值s ,但子構造函數尚未完成。 情況看起來像這樣

class UniformElement {
  def <init>(str: String) = {
    super.<init>()
    s = str
  }
}

在這里可以查看是否將super.<init>()替換為

val someProperty: String = {
  println("=== Element")
  contents(0)
}

它執行之前

s = str

初始化順序問題通常可以通過將急切的val s變為lazy來解決,就像這樣

class UniformElement(str: String) extends Element {
  lazy val s = str
  println("=== UniformElement.s " + s)

  def contents = Array(s)
}

現在輸出

=== Element
=== UniformElement.s str
str

感謝您提出有趣的問題! 我的猜測(花了一些時間在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

如有需要,請隨時澄清。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM