Wenn Merkmal A B erweitert, erhalten Sie durch Einmischen von A genau B plus alles, was A hinzufügt oder erweitert. Wenn im Gegensatz dazu Merkmal A eine Selbstreferenz hat, die explizit als B eingegeben wird, muss die ultimative übergeordnete Klasse auch B oder einen Nachkommen-Typ von B einmischen (und diese zuerst einmischen , was wichtig ist).
Das ist der wichtigste Unterschied. Im ersten Fall kristallisiert der genaue Typ von B an dem Punkt, an dem A ihn erweitert. Im zweiten Fall kann der Designer der übergeordneten Klasse entscheiden, welche Version von B an dem Punkt verwendet wird, an dem die übergeordnete Klasse zusammengesetzt ist.
Ein weiterer Unterschied besteht darin, dass A und B gleichnamige Methoden bereitstellen. Wenn A B erweitert, überschreibt die Methode von A die von B. Wenn A nach B eingemischt wird, gewinnt einfach die Methode von A.
Die getippte Selbstreferenz gibt Ihnen viel mehr Freiheit; Die Kopplung zwischen A und B ist locker.
AKTUALISIEREN:
Da Sie sich über den Nutzen dieser Unterschiede nicht im Klaren sind ...
Wenn Sie die direkte Vererbung verwenden, erstellen Sie das Merkmal A, das B + A ist. Sie haben die Beziehung in Stein gemeißelt.
Wenn Sie eine typisierte Selbstreferenz verwenden, kann dies jeder, der Ihr Merkmal A in Klasse C verwenden möchte
- Mischen Sie B und dann A in C.
- Mischen Sie einen Subtyp von B und dann A in C.
- Mischen Sie A mit C, wobei C eine Unterklasse von B ist.
Und dies ist nicht die Grenze ihrer Optionen, da Sie mit Scala ein Merkmal direkt mit einem Codeblock als Konstruktor instanziieren können.
Betrachten Sie den Unterschied zwischen dem Gewinn der Methode von A , da A zuletzt gemischt wird, im Vergleich zu A, das B erweitert, Folgendes:
Wenn Sie eine Folge von Merkmalen foo()
einmischen, geht der Compiler bei jedem Aufruf der Methode zum zuletzt eingemischten Merkmal, um foo()
danach zu suchen , und durchläuft (falls nicht gefunden) die Folge nach links, bis er ein Merkmal findet, das implementiert foo()
und verwendet wird Das. A hat auch die Option zum Aufrufen super.foo()
, wodurch auch die Sequenz nach links durchlaufen wird, bis eine Implementierung gefunden wird, und so weiter.
Wenn also A eine typisierte Selbstreferenz zu B hat und der Verfasser von A weiß, dass B implementiert foo()
, kann A anrufen und super.foo()
wissen, dass B es tun wird, wenn nichts anderes vorsieht foo()
. Der Ersteller der Klasse C hat jedoch die Möglichkeit, jedes andere Merkmal, in dem implementiert wird foo()
, zu löschen , und A erhält dies stattdessen.
Auch dies ist viel leistungsfähiger und weniger einschränkend als A, das B erweitert und direkt die Version von B aufruft foo()
.