Відповіді:
Різниця між ними полягає в тому, що a val
виконується, коли він визначений, тоді як a 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
) a 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
Тут, коли значення 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
Але з лінивими валами це прекрасно працює
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', але не старе.
Сподіваюсь, це допомагає.
Ледачий вал найлегше розуміється як " запам'ятоване (без аргументу) деф".
Як і def, лінивий val не оцінюється, поки його не викликають. Але результат зберігається, щоб наступні виклики повернули збережене значення. Запам'ятовуваний результат займає місце у вашій структурі даних, як вал.
Як уже згадували інші, випадки використання для ледачого валу полягають у відкладанні дорогих обчислень, поки вони не знадобляться та зберігають їх результати, а також вирішують певні кругові залежності між величинами.
Ледачі вали насправді реалізуються більш-менш як запам'ятовані деф. Детальну інформацію про їх реалізацію можна прочитати тут:
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
EDIT: також буде працювати наступне:
object Y extends { val x = "Hello" } with X
Це називається "раннім ініціалізатором". Дивіться це питання ТА для отримання більш детальної інформації.
Демонстрація lazy
- як визначено вище - виконання при визначенні проти виконання при зверненні: (використовуючи оболонку шкали 2.12.7)
// 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