A def
kann entweder durch a def
, a val
, a lazy val
oder an implementiert werden object
. Es ist also die abstrakteste Form, ein Mitglied zu definieren. Da Merkmale normalerweise abstrakte Schnittstellen sind, bedeutet die Aussage, dass Sie eine möchten val
, wie die Implementierung funktionieren soll. Wenn Sie nach a fragen val
, kann eine implementierende Klasse a nicht verwenden def
.
A val
wird nur benötigt, wenn Sie eine stabile Kennung benötigen, z. B. für einen pfadabhängigen Typ. Das brauchen Sie normalerweise nicht.
Vergleichen Sie:
trait Foo { def bar: Int }
object F1 extends Foo { def bar = util.Random.nextInt(33) } // ok
class F2(val bar: Int) extends Foo // ok
object F3 extends Foo {
lazy val bar = { // ok
Thread.sleep(5000) // really heavy number crunching
42
}
}
Wenn du hättest
trait Foo { val bar: Int }
Sie würden nicht definieren können F1
oder F3
.
Ok, und um Sie zu verwirren und @ om-nom-nom zu beantworten - die Verwendung von Abstracts val
kann zu Initialisierungsproblemen führen:
trait Foo {
val bar: Int
val schoko = bar + bar
}
object Fail extends Foo {
val bar = 33
}
Fail.schoko // zero!!
Dies ist ein hässliches Problem, das meiner persönlichen Meinung nach in zukünftigen Scala-Versionen behoben werden sollte, indem es im Compiler behoben wird. Ja, derzeit ist dies auch ein Grund, warum man keine abstrakten val
s verwenden sollte.
Bearbeiten (Januar 2016): Sie dürfen eine abstrakte val
Deklaration mit einer lazy val
Implementierung überschreiben , um auch den Initialisierungsfehler zu verhindern.
val
mit a überschreibenlazy val
. Ihre Behauptung, dass Sie nicht erstellen könnten,F3
wennbar
a wäre,val
ist nicht richtig. Das heißt, abstrakte Mitglieder in Merkmalen sollten immerdef
'sval schoko = bar + bar
mitlazy val schoko = bar + bar
. Dies ist eine Möglichkeit, die Initialisierungsreihenfolge zu steuern. Durch die Verwendung vonlazy val
anstelle vondef
in der abgeleiteten Klasse wird außerdem eine Neuberechnung vermieden.val bar: Int
,def bar: Int
Fail.schoko
ist immer noch Null.Ich bevorzuge die Verwendung
val
in Merkmalen, da die Val-Deklaration eine unklare und nicht intuitive Reihenfolge der Initialisierung aufweist. Sie können der bereits funktionierenden Hierarchie ein Merkmal hinzufügen, das alle zuvor funktionierenden Dinge zerstört. Weitere Informationen finden Sie in meinem Thema: Warum wird in nicht endgültigen Klassen ein einfacher Wert verwendet?Sie sollten alle Aspekte der Verwendung dieser Wertdeklarationen berücksichtigen, die Sie letztendlich zu einem Fehler führen.
Update mit komplizierterem Beispiel
Aber es gibt Zeiten, in denen Sie die Verwendung nicht vermeiden konnten
val
. Wie @ 0__ bereits erwähnt hatte, benötigen Sie manchmal eine stabile Kennung unddef
sind keine.Ich würde ein Beispiel geben, um zu zeigen, wovon er sprach:
Dieser Code erzeugt den Fehler:
Wenn Sie sich eine Minute Zeit nehmen, um zu glauben, Sie würden verstehen, dass der Compiler einen Grund hat, sich zu beschweren. In diesem
Access2.access
Fall konnte der Rückgabetyp auf keinen Fall abgeleitet werden.def holder
bedeutet, dass es auf breite Weise umgesetzt werden könnte. Es könnten für jeden Anruf unterschiedliche Inhaber zurückgegeben werden, und diese Inhaber würden unterschiedlicheInner
Typen einbeziehen . Die virtuelle Java-Maschine erwartet jedoch, dass derselbe Typ zurückgegeben wird.quelle
Die Verwendung von def scheint immer etwas umständlich zu sein, da so etwas nicht funktioniert:
Sie erhalten folgende Fehlermeldung:
quelle
var
. Der Punkt ist, wenn es sich um Felder handelt, sollten sie als solche gekennzeichnet werden. Ich denke nur, alles so zu haben, wiedef
es kurzsichtig ist.var
Sie die Kapselung unterbrechen . Die Verwendung von adef
(oder aval
) wird jedoch einer globalen Variablen vorgezogen. Ich denke, was Sie suchen, ist so etwas wie,case class ConcreteEntity(override val id: Int) extends Entity
damit Sie es daraus erstellen können.def create(e: Entity) = ConcreteEntity(1)
Dies ist sicherer, als die Kapselung zu brechen und jeder Klasse zu erlauben, die Entität zu ändern.