scala 惰性 val 有什么作用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7484928/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What does a lazy val do?
提问by kiritsuku
I noticed that Scala provide lazy vals. But I don't get what they do.
我注意到 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
The REPLshows that yis a lazy val, but how is it different from a normal val?
该REPL表明,y是lazy val的,但它是如何从一个正常的不同val?
回答by kiritsuku
The difference between them is, that a valis executed when it is defined whereas a lazy valis executed when it is accessed the first time.
它们之间的区别在于, aval在定义时执行,而 alazy 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 valis executed once and then never again. This can be useful when an operation takes long time to complete and when it is not sure if it is later used.
与方法(用 定义def)相反, alazy 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
Here, when the values xand yare never used, only xunnecessarily wasting resources. If we suppose that yhas 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 defsince we don't want to execute it several times.
在这里,当值x和y从未被使用时,只会x不必要地浪费资源。如果我们假设它y没有副作用并且我们不知道它被访问的频率(从不,一次,数千次),那么声明它是没有用的,def因为我们不想多次执行它。
If you want to know how lazy valsare implemented, see this question.
如果您想知道如何lazy vals实施,请参阅此问题。
回答by Landei
This feature helps not only delaying expensive calculations, but is also useful to construct mutual dependent or cyclic structures. E.g. 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()
回答by Mital Pritmani
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). Next when y is called, it's initialized as well as value of last 'x' is taken into consideration but not the old one.
可以看出,x 在初始化时打印,但 y 在以相同方式初始化时不打印(我在这里有意将 x 作为 var - 来解释 y 何时被初始化)。接下来,当 y 被调用时,它会被初始化并考虑最后一个 'x' 的值,但不考虑旧的值。
Hope this helps.
希望这可以帮助。
回答by tksfz
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. 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.
与 def 一样,惰性 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. You can read about the details of their implementation here:
懒惰的 vals 实际上或多或少地作为 memoized defs 实现。您可以在此处阅读有关其实施的详细信息:
http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html
http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html
回答by Jus12
Also lazyis 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 Ywill now throw null pointer exception, because xis not yet initialized.
The following, however, works fine:
访问Y现在将抛出空指针异常,因为x尚未初始化。但是,以下工作正常:
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 questionfor more details.
这称为“早期初始化程序”。有关更多详细信息,请参阅此 SO 问题。
回答by pjames
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
回答by pjames
scala> lazy val lazyEight = {
| println("I am lazy !")
| 8
| }
lazyEight: Int = <lazy>
scala> lazyEight
I am lazy !
res1: Int = 8
- All vals are initialized during object construction
- Use lazy keyword to defer initialization until first usage
- Attention: Lazy vals are not final and therefore might show performance drawbacks
- 所有 vals 在对象构造期间初始化
- 使用 lazy 关键字将初始化推迟到第一次使用
- 注意: Lazy vals 不是最终的,因此可能会显示性能缺陷

