Un auto-type pour un trait A
:
trait B
trait A { this: B => }
dit que " A
ne peut pas être mélangé dans une classe concrète qui ne s'étend pas également B
" .
En revanche, les éléments suivants:
trait B
trait A extends B
dit que "toute classe (concrète ou abstraite) se mélangeant A
sera également mélangée en B" .
Ces deux déclarations ne signifient-elles pas la même chose? L'auto-type ne semble servir qu'à créer la possibilité d'une simple erreur de compilation.
Qu'est-ce que je rate?
trait A[Self] {this: Self => }
c'est légal,trait A[Self] extends Self
non.Réponses:
Il est principalement utilisé pour l' injection de dépendance , comme dans le modèle de gâteau. Il existe un excellent article couvrant de nombreuses formes différentes d'injection de dépendance dans Scala, y compris le modèle de gâteau. Si vous utilisez Google "Cake Pattern and Scala", vous obtiendrez de nombreux liens, y compris des présentations et des vidéos. Pour l'instant, voici un lien vers une autre question .
Maintenant, quelle est la différence entre un type de soi et l'extension d'un trait, c'est simple. Si vous dites
B extends A
, alorsB
c'est unA
. Lorsque vous utilisez des auto-types,B
nécessite unA
. Il existe deux exigences spécifiques créées avec des auto-types:B
est étendu, alors vous devez mélanger dans unA
.A
.Considérez les exemples suivants:
S'il
Tweeter
s'agissait d'une sous-classe deUser
, il n'y aurait pas d'erreur. Dans le code ci-dessus, nous avions besoin d' un àUser
chaqueTweeter
fois, maisUser
aucun n'était fourniWrong
, nous avons donc eu une erreur. Maintenant, avec le code ci-dessus toujours dans la portée, considérez:Avec
Right
, l'exigence de mélanger unUser
est satisfaite. Cependant, la deuxième exigence mentionnée ci-dessus n'est pas satisfaite: la charge de la miseUser
en œuvre demeure pour les classes / caractères qui s'étendentRight
.Avec les
RightAgain
deux exigences sont satisfaites. AUser
et une implémentation deUser
sont fournis.Pour des cas d'utilisation plus pratiques, veuillez consulter les liens au début de cette réponse! Mais j'espère que vous comprenez maintenant.
la source
trait WarmerComponentImpl extends SensorDeviceComponent with OnOffDeviceComponent
? Cela entraîneraitWarmerComponentImpl
d'avoir ces interfaces. Ils seraient disponibles pour tout ce qui s'étendWarmerComponentImpl
, ce qui est clairement faux, car ce n'est pas unSensorDeviceComponent
, ni unOnOffDeviceComponent
. En tant que type autonome, ces dépendances sont disponibles exclusivement pourWarmerComponentImpl
. UnList
pourrait être utilisé comme unArray
, et vice versa. Mais ce n'est tout simplement pas la même chose.this
avec des types personnels est quelque chose que je méprise, car il masque sans raison l'originalthis
.self: Dep1 with Dep2 =>
.Les auto-types vous permettent de définir des dépendances cycliques. Par exemple, vous pouvez y parvenir:
L'héritage utilisant
extends
ne permet pas cela. Essayer:Dans le livre Odersky, consultez la section 33.5 (chapitre Création de l'interface utilisateur de feuille de calcul) où il mentionne:
J'espère que cela t'aides.
la source
Une différence supplémentaire est que les auto-types peuvent spécifier des types non-classe. Par exemple
Le type auto ici est un type structurel. L'effet est de dire que tout ce qui se mélange dans Foo doit implémenter une unité de retour de méthode sans argument "close". Cela permet des mixins sûrs pour la frappe de canard.
la source
abstract class A extends {def close:Unit}
est équivalent àabstract class A {def close:Unit}
. Il ne s'agit donc pas de types structurels.La section 2.3 "Annotations Selftype" de l'article original Scala de Martin Odersky, Scalable Component Abstractions, explique en fait très bien le but du Selftype au-delà de la composition mixin: fournir une autre manière d'associer une classe à un type abstrait.
L'exemple donné dans l'article était le suivant, et il ne semble pas avoir d'élégant correspondant de sous-classe:
la source
Une autre chose qui n'a pas été mentionnée: comme les auto-types ne font pas partie de la hiérarchie de la classe requise, ils peuvent être exclus de la correspondance de modèles, en particulier lorsque vous effectuez une correspondance exhaustive avec une hiérarchie scellée. Ceci est pratique lorsque vous souhaitez modéliser des comportements orthogonaux tels que:
la source
TL; DR résumé des autres réponses:
Les types que vous étendez sont exposés à des types hérités, mais les auto-types ne le sont pas
par exemple:
class Cow { this: FourStomachs }
vous permet d'utiliser des méthodes disponibles uniquement pour les ruminants, telles quedigestGrass
. Les traits qui prolongent la vache n'auront cependant pas de tels privilèges. D'un autre côté,class Cow extends FourStomachs
exposeradigestGrass
à quiconqueextends Cow
.les auto-types autorisent des dépendances cycliques, l'extension d'autres types ne
la source
Commençons par la dépendance cyclique.
Cependant, la modularité de cette solution n'est pas aussi grande qu'elle pourrait le paraître à première vue, car vous pouvez remplacer les auto-types comme suit:
Bien que, si vous remplacez un membre d'un auto-type, vous perdez l'accès au membre d'origine, qui peut toujours être consulté via super héritage. Donc, ce qui est vraiment gagné en utilisant l'héritage, c'est:
Maintenant, je ne peux pas prétendre comprendre toutes les subtilités du modèle de gâteau, mais il me semble que la principale méthode d'application de la modularité est la composition plutôt que l'héritage ou les types de soi.
La version d'héritage est plus courte, mais la principale raison pour laquelle je préfère l'héritage aux types self est que je trouve beaucoup plus difficile d'obtenir l'ordre d'initialisation correct avec les types self. Cependant, il y a certaines choses que vous pouvez faire avec les types d'individu que vous ne pouvez pas faire avec l'héritage. Les auto-types peuvent utiliser un type alors que l'héritage nécessite un trait ou une classe comme dans:
Vous pouvez même faire:
Bien que vous ne puissiez jamais l'instancier. Je ne vois aucune raison absolue de ne pas pouvoir hériter d'un type, mais je pense certainement qu'il serait utile d'avoir des classes et des traits de constructeur de chemin comme nous avons des traits / classes de constructeur de type. Comme malheureusement
Nous avons ceci:
Ou ca:
Un point qui devrait être davantage souligné est que les traits peuvent étendre les classes. Merci à David Maclver de l'avoir signalé. Voici un exemple de mon propre code:
ScnBase
hérite de la classe Swing Frame, il peut donc être utilisé comme un auto-type puis mélangé à la fin (à l'instanciation). Cependant,val geomR
doit être initialisé avant d'être utilisé par l'héritage de traits. Nous avons donc besoin d'une classe pour appliquer l'initialisation préalable degeomR
. La classeScnVista
peut alors être héritée de plusieurs traits orthogonaux qui peuvent eux-mêmes être hérités. L'utilisation de plusieurs paramètres de type (génériques) offre une autre forme de modularité.la source
la source
Un auto-type vous permet de spécifier quels types sont autorisés à mélanger dans un trait. Par exemple, si vous avez un trait avec un auto-type
Closeable
, alors ce trait sait que les seules choses autorisées à le mélanger doivent implémenter l'Closeable
interface.la source
trait A { self:B => ... }
une déclarationX with A
n'est valide que si X étend B. Oui, vous pouvez direX with A with Q
, où Q ne s'étend pas B, mais je crois que le point de kikibobo était que X est si contraint. Ou ai-je raté quelque chose?Mise à jour: Une différence principale est que les auto-types peuvent dépendre de plusieurs classes (j'admets que c'est un peu le cas du coin). Par exemple, vous pouvez avoir
Cela permet d'ajouter le
Employee
mixage à tout ce qui est une sous-classe dePerson
etExpense
. Bien sûr, cela n'a de sens que s'ilExpense
s'étendPerson
ou vice versa. Le fait est que l'utilisation de self-typesEmployee
peut être indépendante de la hiérarchie des classes dont elle dépend. Il ne se soucie pas de ce qui étend quoi - Si vous changez la hiérarchie deExpense
vsPerson
, vous n'avez pas à modifierEmployee
.la source
dans le premier cas, un sous-trait ou une sous-classe de B peut être mélangé à toutes les utilisations A. Donc B peut être un trait abstrait.
la source