Por que usar val simples em classes não finais
Se a classe não for final, ela pode ser estendida.
Existem duas possibilidades para valores: pode ser anulada e deve ser preguiçosa para ela, pode não ser anulada e deve ser final.
Se val é final - você pode assumir que todos os cálculos sobre ele funcionariam através da hierarquia de classes. Se val puder ser anulado, você deve declarar que é preguiçoso para não ser quebrado após a extensão. Você pode deixar val plain e isso não dá garantias de que seria estendido no caminho certo.
Quais casos de uso implicam o uso de valores simples?
Exemplo de falha de inicialização de classe sem valores lazy
abstract class A {
lazy val x1 : String = throw new Exception()
val x2 : String = "mom"
val x3 : String = x1 + ", " + x2
println("A: " + x3)
}
class B extends A {
override lazy val x1: String = "hello"
println("B: " + x3)
}
class C extends B {
override val x2: String = "dad"
println("C: " + x3)
}
testando:
scala> new B
A: hello, mom
B: hello, mom
res8: B = B@7e2bd615
funciona, mas outras subclasses quebraram a funcionalidade já existente
scala> new C
A: hello, null
B: hello, null
C: hello, null
res5: C = C@52a53948
definindo lazy em x2 corrige o caso:
abstract class A {
lazy val x1 : String = throw new Exception()
lazy val x2 : String = "mom"
val x3 : String = x1 + ", " + x2
println("A: " + x3)
}
class B extends A {
override lazy val x1: String = "hello"
println("B: " + x3)
}
class C extends B {
override lazy val x2: String = "dad"
println("C: " + x3)
}
ordem de inicialização correta:
scala> new C
A: hello, dad
B: hello, dad
C: hello, dad
res6: C = C@5e970110