[英]What does a lazy val 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
REPL顯示y
是一個lazy val
,但它與普通val
有什么不同?
它們之間的區別在於, 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()
我知道答案已經給出,但我寫了一個簡單的例子,讓像我這樣的初學者容易理解:
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' 的值,但不考慮舊的值。
希望這可以幫助。
惰性 val 最容易理解為“記憶化(無參數)定義”。
與 def 一樣,惰性 val 在被調用之前不會被評估。 但結果會被保存,以便后續調用返回保存的值。 記憶的結果會占用數據結構中的空間,就像一個 val。
正如其他人所提到的,惰性 val 的用例是推遲昂貴的計算,直到需要它們並存儲它們的結果,並解決值之間的某些循環依賴關系。
Lazy vals 實際上或多或少是作為 memoized defs 實現的。 您可以在此處閱讀有關其實施的詳細信息:
http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html
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
演示 - 如上所述 - 定義時執行與訪問時執行:(使用 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.