我注意到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是一个惰性值,但它与正常值有何不同?
我注意到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是一个惰性值,但它与正常值有何不同?
当前回答
惰性的演示-如上所定义-定义时执行vs访问时执行:(使用2.12.7 scala shell)
// compiler says this is ok when it is lazy
scala> lazy val t: Int = t
t: Int = <lazy>
//however when executed, t recursively calls itself, and causes a StackOverflowError
scala> t
java.lang.StackOverflowError
...
// when the t is initialized to itself un-lazily, the compiler warns you of the recursive call
scala> val t: Int = t
<console>:12: warning: value t does nothing other than call itself recursively
val t: Int = t
其他回答
这个特性不仅有助于延迟昂贵的计算,而且对于构造相互依赖或循环结构也很有用。例如,这会导致堆栈溢出:
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()
惰性的演示-如上所定义-定义时执行vs访问时执行:(使用2.12.7 scala shell)
// compiler says this is ok when it is lazy
scala> lazy val t: Int = t
t: Int = <lazy>
//however when executed, t recursively calls itself, and causes a StackOverflowError
scala> t
java.lang.StackOverflowError
...
// when the t is initialized to itself un-lazily, the compiler warns you of the recursive call
scala> val t: Int = t
<console>:12: warning: value t does nothing other than call itself recursively
val t: Int = t
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问题。
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
scala> lazy val lazyEight = {
| println("I am lazy !")
| 8
| }
lazyEight: Int = <lazy>
scala> lazyEight
I am lazy !
res1: Int = 8
所有val都在对象构造过程中初始化 使用lazy关键字将初始化推迟到第一次使用 注意:惰性值不是最终值,因此可能会显示性能缺陷