Ein Selbsttyp für ein Merkmal A
:
trait B
trait A { this: B => }
sagt, dass " A
nicht in eine konkrete Klasse gemischt werden kann, die sich nicht auch erstreckt B
" .
Auf der anderen Seite die folgenden:
trait B
trait A extends B
sagt, dass "jede (konkrete oder abstrakte) Klasse, die sich einmischt A
, auch in B einmischt" .
Bedeuten diese beiden Aussagen nicht dasselbe? Der Selbsttyp scheint nur dazu zu dienen, die Möglichkeit eines einfachen Fehlers bei der Kompilierung zu schaffen.
Was vermisse ich?
trait A[Self] {this: Self => }
ist legal,trait A[Self] extends Self
nicht.Antworten:
Es wird hauptsächlich für die Abhängigkeitsinjektion verwendet , z. B. im Kuchenmuster. Es gibt einen großartigen Artikel über viele verschiedene Formen der Abhängigkeitsinjektion in Scala, einschließlich des Kuchenmusters. Wenn Sie "Kuchenmuster und Scala" von Google verwenden, erhalten Sie viele Links, einschließlich Präsentationen und Videos. Im Moment ist hier ein Link zu einer anderen Frage .
Was nun der Unterschied zwischen einem Selbsttyp und der Erweiterung eines Merkmals ist, ist einfach. Wenn Sie sagen
B extends A
, dannB
ist einA
. Wenn Sie sich selbst Typen verwenden,B
erfordert einA
. Es gibt zwei spezifische Anforderungen, die mit Selbsttypen erstellt werden:B
verlängert wird, dann sind Sie erforderlich , um Verwechslungen in einA
.A
.Betrachten Sie die folgenden Beispiele:
Wenn
Tweeter
eine Unterklasse von wäreUser
, würde es keinen Fehler geben. In dem obigen Code, wir erforderlich ein ,User
wann immerTweeter
verwendet wird, jedoch einUser
nicht zur Verfügung gestellt wurdeWrong
, so dass wir einen Fehler bekamen. Berücksichtigen Sie nun, da der obige Code noch im Gültigkeitsbereich ist:Damit
Right
ist die Anforderung zum Einmischen von aUser
erfüllt. Die oben genannte zweite Anforderung ist jedoch nicht erfüllt: Die UmsetzungslastUser
bleibt weiterhin für Klassen / Merkmale, die sich erstreckenRight
.Mit
RightAgain
beiden Anforderungen sind erfüllt. AUser
und eine Implementierung vonUser
werden bereitgestellt.Weitere praktische Anwendungsfälle finden Sie unter den Links am Anfang dieser Antwort! Aber hoffentlich verstehst du es jetzt.
quelle
trait WarmerComponentImpl extends SensorDeviceComponent with OnOffDeviceComponent
? Das würde dazu führenWarmerComponentImpl
, dass diese Schnittstellen vorhanden sind. Sie wären für alles verfügbar, was erweitert wurdeWarmerComponentImpl
, was eindeutig falsch ist, da es weder einSensorDeviceComponent
noch ein istOnOffDeviceComponent
. Als Selbsttyp stehen diese Abhängigkeiten ausschließlich zur VerfügungWarmerComponentImpl
. AList
könnte alsArray
und umgekehrt verwendet werden. Aber sie sind einfach nicht dasselbe.this
mit Selbsttypen etwas, auf das ich herabschaue, da es das Original ohne guten Grund beschattetthis
.self: Dep1 with Dep2 =>
.Mit Selbsttypen können Sie zyklische Abhängigkeiten definieren. Zum Beispiel können Sie dies erreichen:
Vererbung mit
extends
erlaubt das nicht. Versuchen:Lesen Sie im Odersky-Buch Abschnitt 33.5 (Kapitel zum Erstellen einer Tabellenkalkulations-Benutzeroberfläche), in dem Folgendes erwähnt wird:
Hoffe das hilft.
quelle
Ein zusätzlicher Unterschied besteht darin, dass Selbsttypen Nichtklassentypen angeben können. Zum Beispiel
Der Selbsttyp ist hier ein Strukturtyp. Der Effekt ist zu sagen, dass alles, was in Foo gemischt wird, eine No-Arg-Methode zum Schließen der Methode "close" implementieren muss. Dies ermöglicht sichere Mixins für die Ententypisierung.
quelle
abstract class A extends {def close:Unit}
entsprichtabstract class A {def close:Unit}
. Es handelt sich also nicht um Strukturtypen.Abschnitt 2.3 "Selftype Annotations" von Martin Oderskys Original-Scala-Papier Scalable Component Abstractions erklärt den Zweck des Selftype über die Mixin-Komposition hinaus sehr gut: Bieten Sie eine alternative Möglichkeit, eine Klasse einem abstrakten Typ zuzuordnen.
Das Beispiel in der Arbeit war wie folgt und es scheint keinen eleganten Korrespondenten für Unterklassen zu haben:
quelle
Eine andere Sache, die nicht erwähnt wurde: Da Selbsttypen nicht Teil der Hierarchie der erforderlichen Klasse sind, können sie vom Musterabgleich ausgeschlossen werden, insbesondere wenn Sie einen umfassenden Abgleich mit einer versiegelten Hierarchie durchführen. Dies ist praktisch, wenn Sie orthogonale Verhaltensweisen modellieren möchten, z.
quelle
TL; DR Zusammenfassung der anderen Antworten:
Von Ihnen erweiterte Typen sind geerbten Typen ausgesetzt, Selbsttypen jedoch nicht
Beispiel:
class Cow { this: FourStomachs }
Ermöglicht die Verwendung von Methoden, die nur Wiederkäuern zur Verfügung stehen, zdigestGrass
. Eigenschaften, die Cow erweitern, haben jedoch keine solchen Privilegien. Auf der anderen Seiteclass Cow extends FourStomachs
wirddigestGrass
jeder aussetzen , derextends Cow
.Selbsttypen ermöglichen zyklische Abhängigkeiten, andere Typen nicht
quelle
Beginnen wir mit der zyklischen Abhängigkeit.
Die Modularität dieser Lösung ist jedoch nicht so groß, wie es zunächst erscheinen mag, da Sie Selbsttypen wie folgt überschreiben können:
Wenn Sie jedoch ein Mitglied eines Selbsttyps überschreiben, verlieren Sie den Zugriff auf das ursprüngliche Mitglied, auf das über Super-Vererbung weiterhin zugegriffen werden kann. Was also wirklich durch die Verwendung von Vererbung gewonnen wird, ist:
Jetzt kann ich nicht behaupten, alle Feinheiten des Kuchenmusters zu verstehen, aber es fällt mir auf, dass die Hauptmethode zur Durchsetzung der Modularität eher in der Komposition als in der Vererbung oder in den Selbsttypen besteht.
Die Vererbungsversion ist kürzer, aber der Hauptgrund, warum ich die Vererbung gegenüber Selbsttypen bevorzuge, ist, dass ich es viel schwieriger finde, die Initialisierungsreihenfolge bei Selbsttypen korrekt zu machen. Es gibt jedoch einige Dinge, die Sie mit Selbsttypen tun können, die Sie mit Vererbung nicht tun können. Selbsttypen können einen Typ verwenden, während für die Vererbung ein Merkmal oder eine Klasse wie folgt erforderlich ist:
Sie können sogar tun:
Obwohl Sie es nie instanziieren können. Ich sehe keinen absoluten Grund dafür, nicht von einem Typ erben zu können, aber ich halte es auf jeden Fall für nützlich, Pfadkonstruktorklassen und -merkmale zu haben, da wir Typkonstruktormerkmale / -klassen haben. Da leider
Wir haben das:
Oder dieses:
Ein Punkt, der mehr verstanden werden sollte, ist, dass Eigenschaften Klassen erweitern können. Vielen Dank an David Maclver für diesen Hinweis. Hier ist ein Beispiel aus meinem eigenen Code:
ScnBase
erbt von der Swing Frame-Klasse, sodass sie als Selbsttyp verwendet und am Ende (bei der Instanziierung) eingemischt werden kann. Allerdingsval geomR
muss initialisiert werden , bevor es durch Vererbungseigenschaften verwendet wird . Wir brauchen also eine Klasse, um die vorherige Initialisierung von zu erzwingengeomR
. Die KlasseScnVista
kann dann von mehreren orthogonalen Merkmalen geerbt werden, von denen selbst geerbt werden kann. Die Verwendung mehrerer Typparameter (Generika) bietet eine alternative Form der Modularität.quelle
quelle
Mit einem Selbsttyp können Sie angeben, welche Typen ein Merkmal mischen dürfen. Wenn Sie beispielsweise ein Merkmal mit einem
Closeable
Selbsttyp haben, weiß dieses Merkmal, dass die einzigen Dinge, die es einmischen dürfen, dieCloseable
Schnittstelle implementieren müssen .quelle
trait A { self:B => ... }
ist eine DeklarationX with A
nur gültig, wenn X B erweitert. Ja, Sie können sagenX with A with Q
, wo Q B nicht erweitert, aber ich glaube, Kikibobos Punkt war, dass X so eingeschränkt ist. Oder habe ich etwas verpasst?Update: Ein Hauptunterschied besteht darin, dass Selbsttypen von mehreren Klassen abhängen können (ich gebe zu, dass dies ein bisschen Eckfall ist). Zum Beispiel können Sie haben
Dies ermöglicht das Hinzufügen des
Employee
Mixins zu allem, was eine Unterklasse vonPerson
und istExpense
. Dies ist natürlich nur dann sinnvoll, wenn esExpense
erweitert wirdPerson
oder umgekehrt. Der Punkt ist, dass die Verwendung von SelbsttypenEmployee
unabhängig von der Hierarchie der Klassen sein kann, von denen sie abhängt. Es ist egal, was was erweitert - Wenn Sie die Hierarchie vonExpense
vs wechselnPerson
, müssen Sie keine Änderungen vornehmenEmployee
.quelle
Im ersten Fall kann ein Untermerkmal oder eine Unterklasse von B in jede Verwendung von A eingemischt werden. B kann also ein abstraktes Merkmal sein.
quelle