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执行-仅在声明时执行。
初始化在计算上是昂贵的,并且val很少使用。
lazy val tiresomeValue = {(1 to 1000000).filter(x => x % 113 == 0).sum}
if (scala.util.Random.nextInt > 1000) {
println(tiresomeValue)
}
tiresomeValue需要很长时间才能计算出来,而且并不总是使用它。使其lazy val节省不必要的计算。
解决循环依赖
让我们看一个示例,其中在实例化期间需要同时声明两个对象:
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,可以在初始化引用之前对其进行分配,而不必担心具有未初始化的值。