懒惰的val做什么?
我注意到Scala提供了lazy vals
。 但我不明白他们做什么。
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
,但它与正常的val
不同呢?
它们之间的区别在于,在定义时执行lazy 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 vals
,看到这个问题 。
这个特性不仅有助于延迟昂贵的计算,还有助于构造相互依赖或循环结构。 例如,这会导致堆栈溢出:
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
但与懒惰的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()
另外lazy
没有循环依赖是有用的,如下面的代码所示:
abstract class X { val x: String println ("x is "+x.length) } object Y extends X { val x = "Hello" } Y
现在访问Y
将抛出空指针exception,因为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
这被称为“早期初始化器”。 看到这个问题的更多细节。
懒惰的val最容易理解为“ memoized def”。
就像一个def,一个懒惰的val不会被调用,直到它被调用。 但是结果被保存,以便后续的调用返回保存的值。 记忆的结果在数据结构中占用空间,就像val。
正如其他人所提到的,一个懒惰的val的用例是延迟昂贵的计算,直到它们被需要并存储它们的结果,并且解决值之间的某些循环依赖关系。
事实上,懒惰的vals实际上是作为memoized defs来实现的。 你可以在这里阅读他们的实现细节:
http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html
我明白,答案是给出的,但我写了一个简单的例子,以便像我这样的初学者很容易理解:
var x = { println("x"); 15 } lazy val y = { println("y"); x+1 } println("-----") x = 17 println("y is: " + y)
以上代码的输出是:
x ----- y y is: 18
可以看出,x在初始化的时候被打印出来,但是当它以相同的方式被初始化的时候,y不会被打印出来(我在这里已经把x当成了var来解释什么时候y被初始化了)。 接下来,当y被调用时,它被初始化,以及最后'x'的值被考虑,但不是旧的。
希望这可以帮助。
scala> lazy val lazyEight = { | println("I am lazy !") | 8 | } lazyEight: Int = <lazy> scala> lazyEight I am lazy ! res1: Int = 8
- 所有的vals在对象构造期间被初始化
- 使用lazy关键字推迟初始化,直到第一次使用
- 注意 :懒惰的vals不是最终的,因此可能会performance出性能上的缺陷