我注意到Scala提供了惰性val。但我不明白他们是干什么的。

scala> val x = 15
x: Int = 15

scala> lazy val y = 13
y: Int = <lazy>

scala> x
res0: Int = 15

scala> y
res1: Int = 13

REPL显示y是一个惰性值,但它与正常值有何不同?


当前回答

lazy val最容易理解为“memoized (no-arg) def”。

与def一样,lazy val在调用之前不会计算值。但是保存结果,以便后续调用返回保存的值。记住的结果会占用数据结构中的空间,就像val一样。

正如其他人所提到的,lazy val的用例是将昂贵的计算推迟到需要时,并存储它们的结果,以及解决值之间的某些循环依赖关系。

事实上,惰性val的实现或多或少就像记忆defs一样。你可以在这里阅读它们的实现细节:

http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html

其他回答

这个特性不仅有助于延迟昂贵的计算,而且对于构造相互依赖或循环结构也很有用。例如,这会导致堆栈溢出:

trait Foo { val foo: Foo }
case class Fee extends Foo { val foo = Faa() }
case class Faa extends Foo { val foo = Fee() }

println(Fee().foo)
//StackOverflowException

但是对于lazy vals,它工作得很好

trait Foo { val foo: Foo }
case class Fee extends Foo { lazy val foo = Faa() }
case class Faa extends Foo { lazy val foo = Fee() }

println(Fee().foo)
//Faa()
scala> lazy val lazyEight = {
     |   println("I am lazy !")
     |   8
     | }
lazyEight: Int = <lazy>

scala> lazyEight
I am lazy !
res1: Int = 8

所有val都在对象构造过程中初始化 使用lazy关键字将初始化推迟到第一次使用 注意:惰性值不是最终值,因此可能会显示性能缺陷

lazy在没有循环依赖的情况下也很有用,如下所示:

abstract class X {
  val x: String
  println ("x is "+x.length)
}

object Y extends X { val x = "Hello" }
Y

现在访问Y会抛出空指针异常,因为x还没有初始化。 但是,下面的方法可以很好地工作:

abstract class X {
  val x: String
  println ("x is "+x.length)
}

object Y extends X { lazy val x = "Hello" }
Y

编辑:以下也可以:

object Y extends { val x = "Hello" } with X 

这被称为“早期初始化式”。更多细节请参见这个SO问题。

它们之间的区别是,val是在定义时执行的,而lazy val是在第一次访问时执行的。

scala> val x = { println("x"); 15 }
x
x: Int = 15

scala> lazy val y = { println("y"); 13 }
y: Int = <lazy>

scala> x
res2: Int = 15

scala> y
y
res3: Int = 13

scala> y
res4: Int = 13

与方法(用def定义)相反,lazy val只执行一次,然后就不再执行。当一个操作需要很长时间才能完成,并且不确定以后是否会使用它时,这可能很有用。

scala> class X { val x = { Thread.sleep(2000); 15 } }
defined class X

scala> class Y { lazy val y = { Thread.sleep(2000); 13 } }
defined class Y

scala> new X
res5: X = X@262505b7 // we have to wait two seconds to the result

scala> new Y
res6: Y = Y@1555bd22 // this appears immediately

这里,当x和y值从未被使用时,只有x不必要地浪费资源。如果我们假设y没有副作用,并且我们不知道它被访问的频率(从不,一次,数千次),那么将它声明为def是没有用的,因为我们不想多次执行它。

如果你想知道lazy val是如何实现的,请看这个问题。

lazy val最容易理解为“memoized (no-arg) def”。

与def一样,lazy val在调用之前不会计算值。但是保存结果,以便后续调用返回保存的值。记住的结果会占用数据结构中的空间,就像val一样。

正如其他人所提到的,lazy val的用例是将昂贵的计算推迟到需要时,并存储它们的结果,以及解决值之间的某些循环依赖关系。

事实上,惰性val的实现或多或少就像记忆defs一样。你可以在这里阅读它们的实现细节:

http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html