回答:
これらの違いは、a val
は定義さlazy val
れたときに実行されるのに対し、a は最初にアクセスされたときに実行されるということです。
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
は対照的に、a は一度だけ実行され、その後は二度と実行されません。これは、操作の完了に時間がかかる場合や、後で使用するかどうかが不明な場合に役立ちます。
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
実装方法を知りたい場合は、この質問を参照してください。
Lazy<T>
.NET と比較する
この機能は、高価な計算を遅らせるだけでなく、相互依存構造または循環構造を構築するのにも役立ちます。例えば、これはスタックオーバーフローにつながります:
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」として最も簡単に理解できます。
defと同様に、lazy valは呼び出されるまで評価されません。ただし、結果は保存されるため、以降の呼び出しでは保存された値が返されます。メモ化された結果は、valのようにデータ構造のスペースを占有します。
他の人が述べたように、遅延valの使用例は、高価な計算が必要になるまで延期して結果を格納し、値間の特定の循環依存関係を解決することです。
レイジーヴァルは実際には、多かれ少なかれメモ化されたdefとして実装されています。あなたはここでそれらの実装の詳細について読むことができます:
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
ため、アクセスするとnullポインタ例外がスローされます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シェルを使用)
// 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