A def
można zaimplementować za pomocą a def
, a val
, a lazy val
lub an object
. Jest to więc najbardziej abstrakcyjna forma definiowania członka. Ponieważ cechy są zwykle abstrakcyjnymi interfejsami, stwierdzenie, że chcesz, val
to powiedzenie, jak powinna działać implementacja. Jeśli poprosisz o a val
, klasa implementująca nie może użyć def
.
A val
jest potrzebne tylko wtedy, gdy potrzebujesz stabilnego identyfikatora, np. Dla typu zależnego od ścieżki. To jest coś, czego zwykle nie potrzebujesz.
Porównać:
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
}
}
Gdybyś miał
trait Foo { val bar: Int }
nie byłbyś w stanie zdefiniować F1
lub F3
.
OK, i żeby zmylić Cię i odpowiedzieć @ om-nom-nom - użycie abstrakcyjnych val
s może powodować problemy z inicjalizacją:
trait Foo {
val bar: Int
val schoko = bar + bar
}
object Fail extends Foo {
val bar = 33
}
Fail.schoko
Jest to brzydki problem, który moim zdaniem powinien zniknąć w przyszłych wersjach Scali poprzez naprawienie go w kompilatorze, ale tak, obecnie jest to również powód, dla którego nie należy używać abstrakcyjnych val
s.
Edycja (styczeń 2016 r.): Możesz zastąpić abstrakcyjną val
deklarację lazy val
implementacją, aby również zapobiec niepowodzeniu inicjalizacji.