Что делает ленивый вал?
я заметил, что Scala обеспечивают lazy vals
. Но я не понимаю, что они делают.
scala> val x = 15
x: Int = 15
scala> lazy val y = 13
y: Int = <lazy>
scala> x
res0: Int = 15
scala> y
res1: Int = 13
на REPL видно, что y
это lazy val
, но чем он отличается от нормального val
?
6 ответов
разница между ними в том, что val
выполняется, когда он определен, тогда как 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
реализованы, см. Это вопрос.
эта функция помогает не только задерживать дорогостоящие вычисления, но также полезна для построения взаимозависимых или циклических структур. Е. Г. это приводит к переполнению стека:
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
но с lazy 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 но не старый.
надеюсь, что это помогает.
ленивый вал наиболее легко понять как "memoized (no-arg) def".
Как и def, ленивый val не оценивается, пока он не будет вызван. Но результат сохраняется, так что последующие вызовы возвращают сохраненное значение. Memoized результат занимает место в вашей структуре данных, как val.
Как уже упоминалось, варианты использования для ленивого val-отложить дорогостоящие вычисления до тех пор, пока они не понадобятся и не сохранят свои результаты, а также решить некоторые циклические зависимости между значениями.
Lazy vals фактически реализованы более или менее как memoized defs. Подробнее об их реализации вы можете прочитать здесь:
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
это называется "ранний инициализатор". См.это так вопрос для получения более подробной информации.
scala> lazy val lazyEight = {
| println("I am lazy !")
| 8
| }
lazyEight: Int = <lazy>
scala> lazyEight
I am lazy !
res1: Int = 8
- все val инициализируются во время строительства объекта
- используйте ключевое слово lazy, чтобы отложить инициализацию до первого использования
- внимание: Lazy vals не являются окончательными и поэтому могут показывать недостатки производительности