懶惰的 val

lazy val 是一種語言功能,其中 val 的初始化被延遲,直到第一次訪問它為止。在那之後,它就像一個普通的 val

要使用它,請在 val 之前新增 lazy 關鍵字。例如,使用 REPL:

scala> lazy val foo = {
     |   println("Initializing")
     |   "my foo value"
     | }
foo: String = <lazy>

scala> val bar = {
     |   println("Initializing bar")
     |   "my bar value"
     | }
Initializing bar
bar: String = my bar value

scala> foo
Initializing
res3: String = my foo value

scala> bar
res4: String = my bar value

scala> foo
res5: String = my foo value

此示例演示了執行順序。當宣告 lazy val 時,儲存到 foo 值的所有內容都是一個尚未評估的惰性函式呼叫。當設定常規 val 時,我們看到 println 呼叫 execute,並將值賦給 bar。當我們第一次看到 println 執行時評估 foo 時 - 而不是第二次評估 println 時。同樣,當評估 bar 時,我們看不到 println 執行 - 只有在宣告時才會執行。

何時使用’懶惰'

  1. 初始化計算量很大,並且使用 val 很少見。

    lazy val tiresomeValue = {(1 to 1000000).filter(x => x % 113 == 0).sum}
    if (scala.util.Random.nextInt > 1000) {
      println(tiresomeValue)
    }
    

    tiresomeValue 需要很長時間才能計算出來,而且並不總是使用它。使它成為一個節省 17 可以節省不必要的計算。

  2. 解決迴圈依賴關係

    讓我們看一個示例,其中有兩個物件需要在例項化期間同時宣告:

    object comicBook {
      def main(args:Array[String]): Unit = {
        gotham.hero.talk()
        gotham.villain.talk()
      }
    }
    
    class Superhero(val name: String) {
      lazy val toLockUp = gotham.villain
      def talk(): Unit = {
        println(s"I won't let you win ${toLockUp.name}!")
      }
    }
    
    class Supervillain(val name: String) {
      lazy val toKill = gotham.hero
      def talk(): Unit = {
        println(s"Let me loosen up Gotham a little bit ${toKill.name}!")
      }
    }
    
    object gotham {
      val hero: Superhero = new Superhero("Batman")
      val villain: Supervillain = new Supervillain("Joker")
    }
    

    如果沒有關鍵字 lazy,則各個物件不能是物件的成員。執行這樣的程式將導致這樣的程式。通過使用 lazy,可以在初始化之前分配引用,而不必擔心具有未初始化的值。