A defpode ser implementado por um def, um val, um lazy valou um object. Portanto, é a forma mais abstrata de definir um membro. Como as características são geralmente interfaces abstratas, dizer que você deseja valé dizer como a implementação deve se comportar. Se você solicitar a val, uma classe de implementação não poderá usar a def.
A valé necessário apenas se você precisar de um identificador estável, por exemplo, para um tipo dependente do caminho. Isso é algo que você geralmente não precisa.
Comparar:
trait Foo { def bar: Int }
object F1 extends Foo { def bar = util.Random.nextInt(33) }
class F2(val bar: Int) extends Foo
object F3 extends Foo {
lazy val bar = {
Thread.sleep(5000)
42
}
}
Se você tinha
trait Foo { val bar: Int }
você não seria capaz de definir F1ou F3.
Ok, e para confundi-lo e responder @ om-nom-nom - usar abstract valpode causar problemas de inicialização:
trait Foo {
val bar: Int
val schoko = bar + bar
}
object Fail extends Foo {
val bar = 33
}
Fail.schoko
Este é um problema feio que, em minha opinião pessoal, deveria desaparecer nas futuras versões do Scala corrigindo-o no compilador, mas sim, atualmente esta também é uma razão pela qual não se deve usar vals abstratos .
Editar (janeiro de 2016): Você tem permissão para substituir uma valdeclaração abstrata por uma lazy valimplementação, de modo que também evite a falha de inicialização.