Разница между ними заключается в том, что 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Эта функция помогает не только задерживать дорогостоящие вычисления, но также полезна для построения взаимозависимых или циклических структур. Например, это приводит к переполнению стека:
Но с ленивым вальсом работает нормально
источник
Я понимаю, что ответ дан, но я написал простой пример, чтобы его было легче понять начинающим, таким как я:
Вывод вышеуказанного кода:
Как видно, x печатается, когда инициализируется, а y не печатается, когда инициализируется таким же образом (здесь я намеренно взял x как var - чтобы объяснить, когда инициализируется y). Затем, когда вызывается y, он инициализируется, а также учитывается значение последнего 'x', но не старое.
Надеюсь это поможет.
источник
Ленивый вал легче всего понять как « запоминание (без аргументов)».
Как и def, lazy val не оценивается, пока не будет вызван. Но результат сохраняется, поэтому последующие вызовы возвращают сохраненное значение. Запомненный результат занимает место в вашей структуре данных, как val.
Как уже упоминали другие, варианты использования для отложенного вычисления заключаются в том, чтобы отложить дорогостоящие вычисления до тех пор, пока они не потребуются, и сохранить их результаты, а также решить определенные циклические зависимости между значениями.
Ленивые vals фактически реализованы более или менее как запомненные определения. Вы можете прочитать о деталях их реализации здесь:
http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html
источник
Также
lazy
полезно без циклических зависимостей, как в следующем коде:Доступ
Y
теперь вызовет исключение нулевого указателя, потому чтоx
еще не инициализировано. Следующее, однако, работает нормально:РЕДАКТИРОВАТЬ: следующее также будет работать:
Это называется «ранний инициализатор». Посмотрите этот ТАК вопрос для более подробной информации.
источник
Демонстрация
lazy
- как определено выше - выполнения, когда оно определено, против выполнения при доступе: (с использованием оболочки 2.12.7 scala)источник
источник