A def
puede implementarse mediante a def
, a val
, a lazy val
o an object
. Entonces es la forma más abstracta de definir un miembro. Dado que los rasgos suelen ser interfaces abstractas, decir que quieres una val
es decir cómo debería funcionar la implementación. Si solicita a val
, una clase de implementación no puede usar a def
.
A val
solo es necesario si necesita un identificador estable, por ejemplo, para un tipo dependiente de la ruta. Eso es algo que normalmente no necesitas.
Comparar:
trait Foo { def bar: Int }
object F1 extends Foo { def bar = util.Random.nextInt(33) }
class F2(val bar: Int) extends Foo // ok
object F3 extends Foo {
lazy val bar = {
Thread.sleep(5000)
42
}
}
Si tuvieras
trait Foo { val bar: Int }
no podrías definir F1
o F3
.
Ok, y para confundirte y responder @ om-nom-nom, el uso de val
s abstractos puede causar problemas de inicialización:
trait Foo {
val bar: Int
val schoko = bar + bar
}
object Fail extends Foo {
val bar = 33
}
Fail.schoko
Este es un problema feo que, en mi opinión personal, debería desaparecer en futuras versiones de Scala al arreglarlo en el compilador, pero sí, actualmente esta es también una razón por la que uno no debería usar val
s abstractos .
Editar (enero de 2016): se le permite anular una val
declaración abstracta con una lazy val
implementación, por lo que también evitaría el error de inicialización.