La diferencia entre ellos es que a val
se ejecuta cuando se define, mientras que a lazy val
se ejecuta cuando se accede por primera vez.
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
A diferencia de un método (definido con def
) a lazy val
se ejecuta una vez y luego nunca más. Esto puede ser útil cuando una operación tarda mucho en completarse y cuando no está seguro si se usará más tarde.
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
Aquí, cuando los valores x
y y
nunca se usan, solo se x
desperdician recursos innecesariamente. Si suponemos que y
no tiene efectos secundarios y que no sabemos con qué frecuencia se accede (nunca, una vez, miles de veces), es inútil declararlo def
ya que no queremos ejecutarlo varias veces.
Si desea saber cómo lazy vals
se implementan, consulte esta pregunta .
Lazy<T>
.NETEsta característica ayuda no solo a retrasar los costosos cálculos, sino que también es útil para construir estructuras mutuamente dependientes o cíclicas. Por ejemplo, esto lleva a un desbordamiento de pila:
Pero con vals perezosos funciona bien
fuente
Entiendo que se da la respuesta, pero escribí un ejemplo simple para que sea fácil de entender para principiantes como yo:
La salida del código anterior es:
Como se puede ver, x se imprime cuando se inicializa, pero y no se imprime cuando se inicializa de la misma manera (he tomado x como var intencionalmente aquí, para explicar cuándo se inicializa y). Luego, cuando se llama y, se inicializa y se tiene en cuenta el valor de la última 'x', pero no el anterior.
Espero que esto ayude.
fuente
Un vago val se entiende más fácilmente como una " definición memorable (sin argumentos )".
Al igual que un def, un val vago no se evalúa hasta que se invoca. Pero el resultado se guarda para que las invocaciones posteriores devuelvan el valor guardado. El resultado memorizado ocupa espacio en su estructura de datos, como un val.
Como otros han mencionado, los casos de uso para un val perezoso son diferir cálculos costosos hasta que sean necesarios y almacenar sus resultados, y resolver ciertas dependencias circulares entre valores.
De hecho, los vals perezosos se implementan más o menos como defs memorables. Puede leer sobre los detalles de su implementación aquí:
http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html
fuente
También
lazy
es útil sin dependencias cíclicas, como en el siguiente código:El acceso
Y
ahora arrojará una excepción de puntero nulo, porquex
aún no se ha inicializado. Lo siguiente, sin embargo, funciona bien:EDITAR: lo siguiente también funcionará:
Esto se llama un "inicializador temprano". Vea esta pregunta SO para más detalles.
fuente
Una demostración de
lazy
- como se definió anteriormente - ejecución cuando se define vs ejecución cuando se accede: (usando 2.12.7 scala shell)fuente
fuente