Risposte:
La differenza tra loro è che a val
viene eseguito quando è definito mentre a lazy val
viene eseguito quando si accede per la prima volta.
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
Contrariamente a un metodo (definito con def
) a lazy val
viene eseguito una volta e poi mai più. Ciò può essere utile quando un'operazione richiede molto tempo per il completamento e quando non è sicuro se verrà utilizzata in seguito.
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
Qui, quando i valori x
e y
non vengono mai utilizzati, solo x
sprecare inutilmente risorse. Se supponiamo che y
non abbia effetti collaterali e che non sappiamo con quale frequenza si acceda (mai, una volta, migliaia di volte), è inutile dichiararlo come def
poiché non vogliamo eseguirlo più volte.
Se vuoi sapere come lazy vals
vengono implementate, vedi questa domanda .
Lazy<T>
in .NET
Questa funzione aiuta non solo a ritardare calcoli costosi, ma è anche utile per costruire strutture cicliche o dipendenti reciprocamente. Ad esempio, questo porta a un overflow dello stack:
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
Ma con Val Lazy funziona bene
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()
Capisco che la risposta è stata data, ma ho scritto un semplice esempio per renderlo facile da capire per i principianti come me:
var x = { println("x"); 15 }
lazy val y = { println("y"); x+1 }
println("-----")
x = 17
println("y is: " + y)
L'output del codice sopra è:
x
-----
y
y is: 18
Come si può vedere, x viene stampato quando viene inizializzato, ma y non viene stampato quando viene inizializzato allo stesso modo (ho preso x come var intenzionalmente qui - per spiegare quando y viene inizializzato). Successivamente, quando viene chiamato y, viene preso in considerazione e viene preso in considerazione il valore dell'ultima "x", ma non quello precedente.
Spero che questo ti aiuti.
Un Val pigro è più facilmente inteso come " def memorizzato (no-arg) def".
Come un def, un valore pigro non viene valutato fino a quando non viene invocato. Ma il risultato viene salvato in modo che le invocazioni successive restituiscano il valore salvato. Il risultato memorizzato occupa spazio nella struttura dei dati, come una val.
Come altri hanno già detto, i casi d'uso di un val pigro consistono nel rinviare calcoli costosi fino a quando non sono necessari e archiviare i loro risultati, e per risolvere alcune dipendenze circolari tra i valori.
Le val pigre sono infatti implementate più o meno come def memorizzate. Puoi leggere i dettagli della loro implementazione qui:
http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html
Inoltre lazy
è utile senza dipendenze cicliche, come nel seguente codice:
abstract class X {
val x: String
println ("x is "+x.length)
}
object Y extends X { val x = "Hello" }
Y
L'accesso Y
genererà ora un'eccezione puntatore null, perché x
non è ancora inizializzato. Quanto segue, tuttavia, funziona bene:
abstract class X {
val x: String
println ("x is "+x.length)
}
object Y extends X { lazy val x = "Hello" }
Y
EDIT: funzionerà anche quanto segue:
object Y extends { val x = "Hello" } with X
Questo si chiama "inizializzatore anticipato". Vedi questa domanda SO per maggiori dettagli.
Una dimostrazione di lazy
- come definito sopra - esecuzione quando definita vs esecuzione quando si accede: (usando 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