Scala懒惰的瓦尔

例子

lazy val是一种语言功能,其中a的初始化val会延迟到首次访问它之前。此后,它的作用就像常规的val。

要使用它,请lazy在之前添加关键字val。例如,使用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调用已执行,并且该值已分配给bar。当我们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需要很长时间才能计算出来,而且并不总是使用它。使其lazy val节省不必要的计算。

  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,则各个对象不能成为对象的成员。执行这样的程序会导致程序错误java.lang.NullPointerException。通过使用lazy,可以在初始化引用之前对其进行分配,而不必担心具有未初始化的值。