Frage

Was ist der Vorteil der Verwendung einer abstrakten Klasse anstelle eines Merkmals (abgesehen von Leistung)? Es scheint, wie abstrakte Klassen können durch Züge in den meisten Fällen ersetzt werden.

War es hilfreich?

Lösung

Ich kann mich zwei Unterschiede

  1. Abstrakte Klassen können Konstruktor Parameter sowie Typparameter. Traits kann nur Typ Parameter. Es gab einige Diskussionen, dass in Zukunft auch Züge haben kann Konstruktorparameter
  2. Abstrakte Klassen sind vollständig kompatibel mit Java. Sie können ohne Wrapper sie von Java-Code aufrufen. Traits ist nur dann vollständig interoperabel, wenn sie enthalten keinen Implementierungscode

Andere Tipps

Es gibt einen Abschnitt in Scala in Programmierung genannt „Um Charakterzug, oder nicht zu Zug?“ die Adressen dieser Frage. Da die erste Auflage Online verfügbar ist, ich hoffe, OK ist es hier, die ganze Sache zu zitieren. (Jeder ernsthafte Scala Programmierer sollte das Buch kaufen):

  

Wenn Sie eine wiederverwendbare Sammlung von Verhalten implementieren, werden Sie   müssen entscheiden, ob Sie ein Merkmal oder eine abstrakte Klasse verwenden möchten.   Es gibt keine feste Regel, aber dieser Abschnitt enthält einige Richtlinien   berücksichtigen.

     

Wenn das Verhalten nicht wieder verwendet wird , dann macht es eine konkrete Klasse. Es   nicht wiederverwendbar Verhalten nach allen ist.

     

Wenn es in mehreren wiederverwendet werden könnte, nicht verwandte Klassen , macht es zu einem Merkmale.   Nur Züge in verschiedene Teile der Klassenhierarchie gemischt werden können.

     

Wenn Sie es in Java-Code zu vererben wollen , eine abstrakte Klasse.   Da Züge mit dem Code nicht eine enge Java analog haben, neigt sie zu sein   umständlich aus einem Zug in einer Java-Klasse zu erben. Vererben von einem   Scala-Klasse, mittlerweile ist genau wie von einer Java-Klasse erbt.   Als eine Ausnahme, trait eine Scala mit nur abstrakten Mitgliedern übersetzt   direkt auf eine Java-Schnittstelle, so sollten Sie fühlen sich frei, so zu definieren,   Züge, auch wenn Sie Java-Code von ihm zu erben erwarten. Siehe Kapitel 29   Weitere Informationen über zusammen mit Java und Scala arbeiten.

     

Wenn Sie planen, in kompilierter Form zu verteilen , und man draußen erwarten   von ihm Gruppen Schreibklassen erben könnte lehnen Sie Richtung   eine abstrakte Klasse. Das Problem ist, dass, wenn ein Zug gewinnt oder verliert   Mitglied, alle Klassen, die vererben sie neu kompiliert werden müssen, auch wenn   sie haben sich nicht geändert. Wenn außerhalb Kunden werden nur die nennen   Verhalten, statt von ihm erben, dann eine Eigenschaft verwendet, ist in Ordnung.

     

Wenn Effizienz ist sehr wichtig , neigt zu einer Klasse. Die meisten Java   Runtimes macht einen virtuellen Methodenaufruf einer Klasse Mitglied einer schnelleren   Betrieb als eine Schnittstelle Methodenaufruf. Traits erhalten kompiliert   Schnittstellen und kann daher eine leichte Performance-Overhead bezahlen.   Allerdings sollten Sie diese Wahl nur, wenn Sie wissen, dass das Merkmal   in Frage stellt einen Leistungsengpass und haben Beweise   dass löst stattdessen das Problem tatsächlich eine Klasse verwendet wird.

     

Wenn Sie noch nicht wissen, , nachdem die oben unter Berücksichtigung, dann starten durch   so dass es als ein Merkmal. Sie können jederzeit später ändern, und im Allgemeinen   ein Merkmal mit mehr Optionen offen läßt.

Wie @Mushtaq Ahmed erwähnt, kann ein Merkmal haben noch keine an den primären Konstruktor einer Klasse übergebenen Parameter.

Ein weiterer Unterschied ist die Behandlung von super.

  

Der andere Unterschied zwischen den Klassen und Eigenschaften ist, dass, während in den Klassen, super Anrufe statisch gebunden sind, in Zügen, werden sie dynamisch gebunden. Wenn Sie super.toString in einer Klasse schreiben, wissen Sie genau, welche Implementierung Methode aufgerufen wird. Wenn Sie die gleiche Sache in einem Zuge schreiben, wobei das Verfahren der Umsetzung jedoch zum Aufruf für den Super-Anruf nicht definiert ist, wenn Sie das Merkmal definieren.

Sehen Sie den Rest Kapitel 12 für weitere Details.

Edit 1 (2013):

Es gibt einen feinen Unterschied in der Art und Weise verhält, abstrakte Klassen im Vergleich zu Zügen. Eine der Linearisierungsregeln ist, dass es die Vererbungshierarchie der Klassen erhalten hat, die in der Kette später abstrakte Klassen zu drücken sucht, während Züge glücklich in gemischt werden können. Unter bestimmten Umständen es in letzteren Position der Klasse Linearisierung tatsächlich vorzuziehen ist , so abstrakte Klassen für die verwendet werden könnten. Siehe Zwang Klasse Linearisierung (mixin Reihenfolge) in Scala .

Edit 2 (2018):

Wie der Scala 2.12 Merkmal der Binärkompatibilität Verhalten hat sich geändert. Vor 2.12, Hinzufügen oder ein Mitglied zu dem Merkmal Entfernen erforderlich Neuübersetzung aller Klassen, die das Merkmal erben, auch wenn die Klassen nicht geändert haben. Dies ist auf die Art und Weise Merkmale wurden in JVM codiert.

Wie der Scala 2.12, Züge Kompilierung auf Java Schnittstellen , so die Forderung ein wenig entspannt hat. Wenn das Merkmal eine der folgenden Bedingungen der Fall ist, deren Unterklassen noch Neukompilierung erfordern:

  
      
  • Definieren von Feldern (val oder var, aber eine Konstante ist ok - final val ohne Ergebnistyp)
  •   
  • Aufruf super
  •   
  • initializer Aussagen im Körper
  •   
  • extending eine Klasse
  •   
  • auf Linearisierung verlassen Implementierungen zu finden in der rechten supertrait
  •   

Aber wenn das Merkmal nicht der Fall, können Sie diese nun aktualisieren, ohne die binäre Kompatibilität zu brechen.

Für was auch immer es wert ist, Odersky et al Programmierung in Scala empfiehlt, wenn Sie Zweifel, verwenden Sie Merkmale. Sie können immer sie in abstrakte Klassen später bei Bedarf ändern.

Außer der Tatsache, dass Sie nicht direkt mehrere abstrakte Klassen erweitern können, aber Sie mehrere Eigenschaften in einer Klasse mixin können, ist es erwähnenswert, dass Züge sind stapelbar, da Super-Anrufe in einem Merkmal dynamisch gebunden sind (es bezieht sich auf eine Klasse oder Trait vor aktuellen gemischt).

Von Thomas Antwort in Unterschied zwischen abstrakter Klasse und Trait :

trait A{
    def a = 1
}

trait X extends A{
    override def a = {
        println("X")
        super.a
    }
}  


trait Y extends A{
    override def a = {
        println("Y")
        super.a
    }
}

scala> val xy = new AnyRef with X with Y
xy: java.lang.Object with X with Y = $anon$1@6e9b6a
scala> xy.a
Y
X
res0: Int = 1

scala> val yx = new AnyRef with Y with X
yx: java.lang.Object with Y with X = $anon$1@188c838
scala> yx.a
X
Y
res1: Int = 1

Wenn eine abstrakte Klasse erweitern, dies zeigt, dass die Unterklasse ist von ähnlicher Art. Diese müssen nicht zwangsläufig der Fall, wenn Züge mit, denke ich.

Scala Programmierung die Autoren sagen, dass abstrakte Klassen ein klassisches Objekt machen orientiert "is-a" Beziehung, während Züge eine scala-Art und Weise der Zusammensetzung sind.

Abstrakte Klassen können Verhalten enthalten - Sie mit Konstruktor args parametriert können (die Züge nicht kann) und eine Arbeitseinheit darstellen. Traits statt nur ein einziges Merkmal dar, eine Schnittstelle von einer Funktionalität.

  1. Eine Klasse kann von mehreren Merkmalen erben, aber nur eine abstrakte Klasse.
  2. Abstrakte Klassen können Konstruktor Parameter sowie Typparameter. Traits kann nur Typ Parameter. Zum Beispiel kann man nicht sagen Merkmal t (i: int) {}; der i-Parameter ist illegal.
  3. Abstrakte Klassen sind vollständig kompatibel mit Java. Sie können ohne Wrapper sie von Java-Code aufrufen. Traits sind vollständig kompatibel nur, wenn sie enthalten keinen Implementierungscode.
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top