Beginnen wir mit der Konjunkturabhängigkeit.
trait A {
selfA: B =>
def fa: Int }
trait B {
selfB: A =>
def fb: String }
Die Modularität dieser Lösung ist jedoch nicht so groß, wie es auf den ersten Blick erscheinen mag, denn Sie können Self-Typen auch überschreiben:
trait A1 extends A {
selfA1: B =>
override def fb = "B's String" }
trait B1 extends B {
selfB1: A =>
override def fa = "A's String" }
val myObj = new A1 with B1
Wenn Sie jedoch ein Element eines Selbsttyps überschreiben, verlieren Sie den Zugriff auf das ursprüngliche Element, auf das jedoch weiterhin über Super mittels Vererbung zugegriffen werden kann. Was also wirklich über die Verwendung von Vererbung gewonnen wird, ist:
trait AB {
def fa: String
def fb: String }
trait A1 extends AB
{ override def fa = "A's String" }
trait B1 extends AB
{ override def fb = "B's String" }
val myObj = new A1 with B1
Nun kann ich nicht behaupten, alle Feinheiten des Kuchenmusters zu verstehen, aber mir scheint, dass die Hauptmethode zur Durchsetzung der Modularität eher durch Komposition als durch Vererbung oder Selbsttypen erfolgt.
Die Vererbungsversion ist kürzer, aber der Hauptgrund, warum ich die Vererbung den Selbsttypen vorziehe, ist, dass ich es viel schwieriger finde, die Initialisierungsreihenfolge bei Selbsttypen richtig hinzubekommen. Allerdings gibt es einige Dinge, die man mit Selbsttypen machen kann, die man mit Vererbung nicht machen kann. Selbsttypen können einen Typ verwenden, während die Vererbung einen Trait oder eine Klasse erfordert, wie in:
trait Outer
{ type T1 }
trait S1
{ selfS1: Outer#T1 => } //Not possible with inheritance.
Das können Sie auch tun:
trait TypeBuster
{ this: Int with String => }
Allerdings werden Sie ihn nie instanziieren können. Ich sehe keinen absoluten Grund dafür, dass man nicht von einem Typ erben kann, aber ich denke, dass es nützlich wäre, Pfadkonstruktorklassen und -traits zu haben, so wie wir Typkonstruktortraits/-klassen haben. Wie leider
trait InnerA extends Outer#Inner //Doesn't compile
Wir haben das hier:
trait Outer
{ trait Inner }
trait OuterA extends Outer
{ trait InnerA extends Inner }
trait OuterB extends Outer
{ trait InnerB extends Inner }
trait OuterFinal extends OuterA with OuterB
{ val myV = new InnerA with InnerB }
Oder dies:
trait Outer
{ trait Inner }
trait InnerA
{this: Outer#Inner =>}
trait InnerB
{this: Outer#Inner =>}
trait OuterFinal extends Outer
{ val myVal = new InnerA with InnerB with Inner }
Ein Punkt, der stärker beachtet werden sollte, ist, dass Traits Klassen erweitern können. Vielen Dank an David Maclver für den Hinweis darauf. Hier ist ein Beispiel aus meinem eigenen Code:
class ScnBase extends Frame
abstract class ScnVista[GT <: GeomBase[_ <: TypesD]](geomRI: GT) extends ScnBase with DescripHolder[GT] )
{ val geomR = geomRI }
trait EditScn[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]
trait ScnVistaCyl[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]
ScnBase
erbt von der Schaukel Frame-Klasse, so dass sie als eigener Typ verwendet und dann am Ende (bei der Instanziierung) hinzugefügt werden kann. Allerdings, val geomR
muss initialisiert werden, bevor es von vererbenden Merkmalen verwendet wird. Wir brauchen also eine Klasse, die eine vorherige Initialisierung von geomR
. Die Klasse ScnVista
kann dann von mehreren orthogonalen Merkmalen geerbt werden, die ihrerseits von diesen geerbt werden können. Die Verwendung mehrerer Typparameter (Generika) bietet eine alternative Form der Modularität.
0 Stimmen
Ich interessiere mich eigentlich für die Unterschiede zwischen Selbsttypen und Unterklassen in Traits. Ich kenne einige der üblichen Verwendungszwecke für Selbsttypen; ich kann nur keinen Grund finden, warum sie nicht auf die gleiche Weise mit Subtypisierung durchgeführt werden sollten.
35 Stimmen
Man kann Typparameter innerhalb von Selbsttypen verwenden:
trait A[Self] {this: Self => }
ist legal,trait A[Self] extends Self
ist es nicht.4 Stimmen
Ein Selbsttyp kann auch eine Klasse sein, aber ein Trait kann nicht von einer Klasse erben.
10 Stimmen
@cvogt: ein Trait kann von einer Klasse erben (zumindest ab 2.10): pastebin.com/zShvr8LX
1 Stimmen
@Blaisorblade: Ist das nicht etwas, das durch eine kleine Umgestaltung der Sprache gelöst werden könnte, und keine grundlegende Einschränkung? (zumindest vom Standpunkt der Frage aus gesehen)
0 Stimmen
@ErikAllik: Ich habe von dieser Einschränkung aus dem Papier erfahren, in dem das Kuchenmuster "skalierbare Komponentenabstraktionen" beschrieben wird, daher bezweifle ich, dass das ein Zufall ist. Ich vermute, dass die Gründe einfach auf die Einschränkungen der JVM zurückzuführen sind und nicht auf tiefere Beweggründe, aber das bedeutet nicht unbedingt, dass eine Lösung möglich ist.
0 Stimmen
Ich fand diesen Selbsttyp sehr nützlich, um Klassen, die einen Trait implementieren, mitzuteilen, dass sie einen (versiegelten) trait-Enum . Siehe dies: stackoverflow.com/q/36066238/1206998