Różnica między nimi polega na tym, że a val
jest wykonywane, gdy jest zdefiniowane, a a lazy val
jest wykonywane, gdy jest uzyskiwane po raz pierwszy.
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
W przeciwieństwie do metody (zdefiniowanej za pomocą def
) a lazy val
jest wykonywane raz, a potem nigdy więcej. Może to być przydatne, gdy operacja zajmuje dużo czasu i nie ma pewności, czy zostanie później użyta.
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
Tutaj, gdy wartości x
i y
nigdy nie są używane, tylko x
niepotrzebnie marnuje zasoby. Jeśli przypuszczamy, że y
nie ma żadnych skutków ubocznych i nie wiemy, jak często jest on uzyskiwany (nigdy, raz, tysiące razy), nie ma sensu go deklarować, def
ponieważ nie chcemy go wykonywać kilka razy.
Jeśli chcesz wiedzieć, jak lazy vals
są realizowane, zobacz to pytanie .
Lazy<T>
w .NETTa funkcja pomaga nie tylko opóźniać kosztowne obliczenia, ale jest także przydatna do budowy wzajemnie zależnych lub cyklicznych struktur. Np. Prowadzi to do przepełnienia stosu:
Ale w przypadku leniwych vals działa dobrze
źródło
Rozumiem, że odpowiedź jest podana, ale napisałem prosty przykład, aby ułatwić zrozumienie dla początkujących takich jak ja:
Wyjście powyższego kodu to:
Jak widać, x jest drukowane podczas inicjalizacji, ale y nie jest drukowane, gdy jest inicjowany w ten sam sposób (wziąłem x jako var celowo tutaj - aby wyjaśnić, kiedy y jest inicjalizowany). Następnie, gdy wywoływane jest y, jest ono inicjalizowane, a także uwzględniana jest wartość ostatniego „x”, ale nie stara.
Mam nadzieję że to pomoże.
źródło
Leniwy val jest najłatwiejszy do zrozumienia jako „ zapamiętany (bez arg) def”.
Podobnie jak def, leniwa val nie jest oceniana, dopóki nie zostanie wywołana. Ale wynik jest zapisywany, więc kolejne wywołania zwracają zapisaną wartość. Zapamiętany wynik zajmuje miejsce w strukturze danych, podobnie jak val.
Jak wspomnieli inni, przypadki użycia leniwej wartości polegają na odkładaniu drogich obliczeń, aż będą potrzebne i na przechowywaniu ich wyników oraz na rozwiązaniu pewnych cyklicznych zależności między wartościami.
Leniwe vale są w rzeczywistości implementowane mniej więcej jako zapamiętane def. O szczegółach ich wdrożenia możesz przeczytać tutaj:
http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html
źródło
lazy
Jest także przydatny bez cyklicznych zależności, jak w poniższym kodzie:Uzyskiwanie dostępu
Y
spowoduje teraz zgłoszenie wyjątku zerowego wskaźnika, ponieważx
nie został jeszcze zainicjowany. Działa to jednak dobrze:EDYCJA: działają również następujące elementy:
Nazywa się to „wczesnym inicjatorem”. Zobacz to SO pytanie, aby uzyskać więcej informacji.
źródło
Demonstracja
lazy
- jak zdefiniowano powyżej - wykonania po zdefiniowaniu vs wykonanie przy dostępie: (przy użyciu powłoki Scala 2.12.7)źródło
źródło