A def
може бути реалізований будь-яким з a def
, a val
, a lazy val
або an object
. Отже, це найбільш абстрактна форма визначення члена. Оскільки риси, як правило, є абстрактними інтерфейсами, сказати, що ви хочете, val
це сказати, як має бути реалізація. Якщо ви просите a val
, клас, що реалізує, не може використовувати adef
.
A val
потрібен лише в тому випадку, якщо вам потрібен стабільний ідентифікатор, наприклад для типу, що залежить від шляху. Це те, що вам зазвичай не потрібно.
Порівняйте:
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
}
}
Якби у вас було
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
реалізацією, щоб це також запобігло помилці ініціалізації.