A diferença entre eles é que a val
é executado quando definido, enquanto a lazy val
é executado quando acessado pela primeira 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
Ao contrário de um método (definido com def
), a lazy val
é executado uma vez e nunca mais. Isso pode ser útil quando uma operação leva muito tempo para ser concluída e quando não se tem certeza se será usada posteriormente.
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
Aqui, quando os valores x
e y
nunca são usados, apenas x
desperdiçando recursos desnecessariamente. Se supusermos que y
isso não tem efeitos colaterais e que não sabemos com que frequência ele é acessado (nunca, uma vez, milhares de vezes), é inútil declará-lo, def
pois não queremos executá-lo várias vezes.
Se você quiser saber como lazy vals
são implementadas, consulte esta pergunta .
Lazy<T>
.NETEsse recurso ajuda não apenas a atrasar cálculos caros, mas também é útil para construir estruturas dependentes ou cíclicas mútuas. Por exemplo, isso leva a um estouro de pilha:
Mas com vals preguiçosos, funciona bem
fonte
Entendo que a resposta foi dada, mas escrevi um exemplo simples para facilitar o entendimento de iniciantes como eu:
A saída do código acima é:
Como pode ser visto, x é impresso quando inicializado, mas y não é impresso quando inicializado da mesma maneira (tomei x como var intencionalmente aqui - para explicar quando y é inicializado). Em seguida, quando y é chamado, é inicializado, assim como o valor do último 'x' é levado em consideração, mas não o antigo.
Espero que isto ajude.
fonte
Um val preguiçoso é mais facilmente entendido como um " def memorizado (sem argumento)".
Como um def, um val preguiçoso não é avaliado até ser invocado. Mas o resultado é salvo para que as chamadas subseqüentes retornem o valor salvo. O resultado memorizado ocupa espaço em sua estrutura de dados, como um valor.
Como outros já mencionaram, os casos de uso de um val lento são adiar cálculos caros até que sejam necessários e armazenar seus resultados e resolver certas dependências circulares entre valores.
Vals preguiçosos são de fato implementados mais ou menos como padrões memorizados. Você pode ler sobre os detalhes de sua implementação aqui:
http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html
fonte
Também
lazy
é útil sem dependências cíclicas, como no código a seguir:O acesso
Y
agora lançará uma exceção de ponteiro nulo, porquex
ainda não foi inicializado. O seguinte, no entanto, funciona bem:EDIT: o seguinte também funcionará:
Isso é chamado de "inicializador inicial". Veja esta pergunta para mais detalhes.
fonte
Uma demonstração de
lazy
- como definido acima - execução quando definida vs execução quando acessada: (usando o shell scala 2.12.7)fonte
fonte