Quel est l'avantage d'utiliser une classe abstraite au lieu d'un trait (en dehors de la performance)? Il semble que les classes abstraites puissent être remplacées par des traits dans la plupart des cas.
371
Je peux penser à deux différences
Il y a une section dans Programmation dans Scala appelée "Pour trait, ou pas pour trait?" qui répond à cette question. Puisque la 1ère édition est disponible en ligne, j'espère que c'est bien de citer le tout ici. (Tout programmeur Scala sérieux devrait acheter le livre):
Comme l'a mentionné @Mushtaq Ahmed, un trait ne peut avoir aucun paramètre transmis au constructeur principal d'une classe.
Une autre différence est le traitement des
super
.Voir le reste du chapitre 12 pour plus de détails.
Édition 1 (2013):
Il existe une différence subtile dans le comportement des classes abstraites par rapport aux traits. L'une des règles de linéarisation est qu'elle préserve la hiérarchie d'héritage des classes, ce qui tend à pousser les classes abstraites plus tard dans la chaîne tandis que les traits peuvent être mélangés avec bonheur. Dans certaines circonstances, il est en fait préférable d'être dans la dernière position de la linéarisation de classe , donc des classes abstraites pourraient être utilisées pour cela. Voir contrainte de linéarisation de classe (ordre de mixage) dans Scala .
Édition 2 (2018):
Depuis Scala 2.12, le comportement de compatibilité binaire de trait a changé. Avant la 2.12, l'ajout ou la suppression d'un membre au trait nécessitait une recompilation de toutes les classes héritant du trait, même si les classes n'avaient pas changé. Cela est dû à la façon dont les traits ont été encodés dans JVM.
Depuis Scala 2.12, les traits se compilent vers les interfaces Java , donc l'exigence s'est un peu relâchée. Si le trait fait l'une des actions suivantes, ses sous-classes nécessitent toujours une recompilation:
Mais si le trait ne fonctionne pas, vous pouvez maintenant le mettre à jour sans rompre la compatibilité binaire.
la source
If outside clients will only call into the behavior, instead of inheriting from it, then using a trait is fine
- Quelqu'un pourrait-il expliquer quelle est la différence ici?extends
vswith
?extends
etwith
. C'est purement syntaxique. Si vous héritez de plusieurs modèles, le premier obtientextend
, tous les autres obtiennentwith
, c'est tout. Pensezwith
comme une virgule:class Foo extends Bar, Baz, Qux
.Pour tout ce que cela vaut, la programmation d'Odersky et al dans Scala recommande que, en cas de doute, vous utilisez des traits. Vous pouvez toujours les changer en classes abstraites plus tard si nécessaire.
la source
Outre le fait que vous ne pouvez pas étendre directement plusieurs classes abstraites, mais que vous pouvez mélanger plusieurs traits dans une classe, il convient de mentionner que les traits sont empilables, car les super-appels dans un trait sont liés dynamiquement (il s'agit d'une classe ou d'un trait mélangé avant l'actuel).
Extrait de la réponse de Thomas dans Différence entre classe abstraite et caractère :
la source
Lors de l'extension d'une classe abstraite, cela montre que la sous-classe est d'un type similaire. Ce n'est pas nécessairement le cas lors de l'utilisation de traits, je pense.
la source
Dans Programming Scala, les auteurs disent que les classes abstraites établissent une relation classique "est-un" orientée objet tandis que les traits sont une voie de composition scala.
la source
Les classes abstraites peuvent contenir un comportement - Elles peuvent être paramétrées avec des arguments de constructeur (quels traits ne peuvent pas) et représenter une entité fonctionnelle. Les traits représentent plutôt une seule fonctionnalité, une interface d'une fonctionnalité.
la source
trait Enumerable
avec beaucoup de fonctions d'assistance, je ne les appellerais pas comportement mais simplement des fonctionnalités liées à une seule fonctionnalité.la source