A defpuede implementarse mediante a def, a val, a lazy valo 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 vales decir cómo debería funcionar la implementación. Si solicita a val, una clase de implementación no puede usar a def.
A valsolo 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 F1o F3.
Ok, y para confundirte y responder @ om-nom-nom, el uso de vals 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 vals abstractos .
Editar (enero de 2016): se le permite anular una valdeclaración abstracta con una lazy valimplementación, por lo que también evitaría el error de inicialización.