Si le trait A prolonge B, alors le mélange dans A vous donne précisément B plus tout ce que A ajoute ou étend. En revanche, si trait A a une référence auto qui est explicitement typé B, puis la classe mère ultime doit également mélanger B ou un type descendant de B (et mélanger en premier , ce qui est important).
C'est la différence la plus importante. Dans le premier cas, le type précis de B est cristallisé au point A où il se prolonge. Dans le second, le concepteur de la classe parent doit décider quelle version de B est utilisée, au moment où la classe parent est composée.
Une autre différence est où A et B fournissent des méthodes du même nom. Lorsque A étend B, la méthode de A remplace B. Lorsque A est mélangé après B, la méthode de A gagne simplement.
L'auto référence saisie vous donne beaucoup plus de liberté; le couplage entre A et B est lâche.
MISE À JOUR:
Puisque vous n'êtes pas clair sur les avantages de ces différences ...
Si vous utilisez l'héritage direct, vous créez le trait A qui est B + A. Vous avez fixé la relation dans la pierre.
Si vous utilisez une auto-référence typée, toute personne qui souhaite utiliser votre trait A dans la classe C pourrait
- Mélanger B puis A dans C.
- Mélanger un sous-type de B puis A dans C.
- Mélanger A dans C, où C est une sous-classe de B.
Et ce n'est pas la limite de leurs options, étant donné la façon dont Scala vous permet d'instancier un trait directement avec un bloc de code comme constructeur.
Quant à la différence entre la méthode gagnante de A , parce que A est mélangé en dernier, par rapport à A qui étend B, considérez ceci ...
Lorsque vous mélangez dans une séquence de traits, chaque fois que la méthode foo()
est invoquée, le compilateur va au dernier trait mélangé à rechercher foo()
, puis (s'il n'est pas trouvé), il parcourt la séquence vers la gauche jusqu'à ce qu'il trouve un trait qui implémente foo()
et utilise cette. A a également la possibilité d'appeler super.foo()
, qui parcourt également la séquence vers la gauche jusqu'à ce qu'il trouve une implémentation, etc.
Donc, si A a une auto-référence typée à B et que l'auteur de A sait que B implémente foo()
, A peut appeler super.foo()
sachant que si rien d'autre ne le prévoit foo()
, B le fera. Cependant, le créateur de la classe C a la possibilité de supprimer tout autre trait dans lequel il est implémenté foo()
, et A l'obtiendra à la place.
Encore une fois, c'est beaucoup plus puissant et moins limitatif que A étendant B et appelant directement la version de B de foo()
.