懒惰的 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,可以在初始化之前分配引用,而不必担心具有未初始化的值。