簡體   English   中英

惰性 val 有什么作用?

[英]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

在這里,當值xy從未被使用時,只有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
  • 所有 vals 在對象構造期間初始化
  • 使用 lazy 關鍵字將初始化推遲到第一次使用
  • 注意: Lazy vals 不是最終的,因此可能會顯示性能缺陷

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM