A def
может быть реализован с помощью a def
, a val
, a lazy val
или an object
. Так что это наиболее абстрактная форма определения члена. Поскольку черты обычно являются абстрактными интерфейсами, то, что вы хотите, val
означает, как должна работать реализация. Если вы запрашиваете a val
, реализующий класс не может использовать def
.
A val
нужен только в том случае, если вам нужен стабильный идентификатор, например, для типа, зависящего от пути. Это то, что вам обычно не нужно.
Сравните:
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
}
}
Если у тебя есть
trait Foo { val bar: Int }
вы не сможете определить F1
или F3
.
Хорошо, и чтобы запутать вас, ответьте @ om-nom-nom - использование абстрактных val
s может вызвать проблемы с инициализацией:
trait Foo {
val bar: Int
val schoko = bar + bar
}
object Fail extends Foo {
val bar = 33
}
Fail.schoko
Это уродливая проблема, которая, по моему мнению, должна исчезнуть в будущих версиях Scala, исправив ее в компиляторе, но да, в настоящее время это также причина, по которой не следует использовать абстрактные val
s.
Изменить (январь 2016 г.): вам разрешено переопределить абстрактное val
объявление с помощью lazy val
реализации, чтобы также предотвратить сбой инициализации.