[英]What does a lazy val do?
I noticed that Scala provide lazy vals
.我注意到 Scala 提供了
lazy vals
。 But I don't get what they do.但我不明白他们在做什么。
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
The REPL shows that y
is a lazy val
, but how is it different from a normal val
? REPL显示
y
是一个lazy val
,但它与普通val
有什么不同?
The difference between them is, that a val
is executed when it is defined whereas a lazy val
is executed when it is accessed the first time.它们之间的区别在于,
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
In contrast to a method (defined with def
) a lazy val
is executed once and then never again.与方法(用
def
定义)相反, lazy val
执行一次,然后再也不执行。 This can be useful when an operation takes long time to complete and when it is not sure if it is later used.当操作需要很长时间才能完成并且不确定以后是否会使用时,这会很有用。
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
Here, when the values x
and y
are never used, only x
unnecessarily wasting resources.在这里,当值
x
和y
从未被使用时,只有x
不必要地浪费资源。 If we suppose that y
has no side effects and that we do not know how often it is accessed (never, once, thousands of times) it is useless to declare it as def
since we don't want to execute it several times.如果我们假设
y
没有副作用并且我们不知道它被访问的频率(从不,一次,数千次),那么将它声明为def
是没有用的,因为我们不想多次执行它。
If you want to know how lazy vals
are implemented, see this question .如果您想知道
lazy vals
是如何实现的,请参阅此问题。
This feature helps not only delaying expensive calculations, but is also useful to construct mutual dependent or cyclic structures.此功能不仅有助于延迟昂贵的计算,而且对于构建相互依赖或循环结构也很有用。 Eg this leads to an stack overflow:
例如,这会导致堆栈溢出:
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
But with lazy vals it works fine但是使用惰性 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()
I understand that the answer is given but I wrote a simple example to make it easy to understand for beginners like me:我知道答案已经给出,但我写了一个简单的例子,让像我这样的初学者容易理解:
var x = { println("x"); 15 }
lazy val y = { println("y"); x + 1 }
println("-----")
x = 17
println("y is: " + y)
Output of above code is:上面代码的输出是:
x
-----
y
y is: 18
As it can be seen, x is printed when it's initialized, but y is not printed when it's initialized in same way (I have taken x as var intentionally here - to explain when y gets initialized).可以看出,x 在初始化时打印,但 y 在以相同方式初始化时不打印(我在这里特意将 x 作为 var - 来解释 y 何时被初始化)。 Next when y is called, it's initialized as well as value of last 'x' is taken into consideration but not the old one.
接下来,当 y 被调用时,它会被初始化并考虑最后一个 'x' 的值,但不考虑旧的值。
Hope this helps.希望这可以帮助。
A lazy val is most easily understood as a " memoized (no-arg) def".惰性 val 最容易理解为“记忆化(无参数)定义”。
Like a def, a lazy val is not evaluated until it is invoked.与 def 一样,惰性 val 在被调用之前不会被评估。 But the result is saved so that subsequent invocations return the saved value.
但结果会被保存,以便后续调用返回保存的值。 The memoized result takes up space in your data structure, like a val.
记忆的结果会占用数据结构中的空间,就像一个 val。
As others have mentioned, the use cases for a lazy val are to defer expensive computations until they are needed and store their results, and to solve certain circular dependencies between values.正如其他人所提到的,惰性 val 的用例是推迟昂贵的计算,直到需要它们并存储它们的结果,并解决值之间的某些循环依赖关系。
Lazy vals are in fact implemented more or less as memoized defs. Lazy vals 实际上或多或少是作为 memoized defs 实现的。 You can read about the details of their implementation here:
您可以在此处阅读有关其实施的详细信息:
http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html
Also lazy
is useful without cyclic dependencies, as in the following code: lazy
在没有循环依赖的情况下也很有用,如以下代码所示:
abstract class X {
val x: String
println ("x is "+x.length)
}
object Y extends X { val x = "Hello" }
Y
Accessing Y
will now throw null pointer exception, because x
is not yet initialized.访问
Y
现在将抛出空指针异常,因为x
尚未初始化。 The following, however, works fine:但是,以下工作正常:
abstract class X {
val x: String
println ("x is "+x.length)
}
object Y extends X { lazy val x = "Hello" }
Y
EDIT: the following will also work:编辑:以下也将起作用:
object Y extends { val x = "Hello" } with X
This is called an "early initializer".这称为“早期初始化程序”。 See this SO question for more details.
有关更多详细信息,请参阅此 SO 问题。
A demonstration of lazy
- as defined above - execution when defined vs execution when accessed: (using 2.12.7 scala shell) lazy
演示 - 如上所述 - 定义时执行与访问时执行:(使用 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
scala> lazy val lazyEight = {
| println("I am lazy !")
| 8
| }
lazyEight: Int = <lazy>
scala> lazyEight
I am lazy !
res1: Int = 8
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.