我注意到Scala提供了lazy vals
。但我不明白他們做了什麼。懶惰的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
是lazy val
,但它是如何從一個普通val
不同?
我注意到Scala提供了lazy vals
。但我不明白他們做了什麼。懶惰的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
是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
相反到lazy val
被執行一次,然後再也的方法(與def
定義)。當操作需要很長時間才能完成,以及何時不確定是否稍後使用時,這可能非常有用。
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 = [email protected] // we have to wait two seconds to the result
scala> new Y
res6: Y = [email protected] // this appears immediately
這裏,當從來沒有使用過的值x
和y
,只有x
不必要的資源浪費。如果我們假設y
沒有副作用,並且我們不知道它被訪問的頻率(從來沒有,一次,幾千次),將它宣佈爲def
是沒有用的,因爲我們不想多次執行它。
如果您想知道lazy vals
是如何實現的,請參閱此question。
此功能不僅有助於延遲昂貴的計算,還有助於構建相互依賴或循環結構。例如。這導致一個堆棧溢出:
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
但隨着懶瓦爾斯它工作正常
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()
但是,如果您的toString方法輸出」foo「,將導致相同的StackOverflowException,屬性。無論如何,「懶惰」的好例子! –
而且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
這就是所謂的 「早期初始化」。有關更多詳細信息,請參閱this SO question。
惰性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被打印出來,但是,當它的INI不打印ÿ以相同的方式進行tialized(我已經在這裏故意將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
作爲補充:@ViktorKlang在Twitter上發佈:[「鮮爲人知的斯卡拉事實:如果初始化一個懶惰的val拋出一個excel 它會嘗試在下次訪問時重新初始化val。「](https://twitter.com/#!/viktorklang/status/104483846002704384) –