Frage

In Scala, ich habe die Konstrukte gesehen

trait T extends S

und

trait T { this: S =>

verwendet, um ähnliche Dinge zu erreichen (nämlich, dass die abstrakten Methoden in S definiert werden müssen, bevor eine Instanz erstellt werden kann). Was ist der Unterschied zwischen ihnen? Warum Sie einen über den anderen verwenden würde?

War es hilfreich?

Lösung

würde ich selbstTypen für Abhängigkeitsmanagement verwenden: Diese Eigenschaft erfordert ein anderes Merkmal in gemischt werden, und ich Vererbung würde eine andere Eigenschaft oder eine Schnittstelle verfeinern

..

Nur als Beispiel:

trait FooService

trait FooRemoting { this : FooService => }
trait FooPersistence { this : FooService => }

object Services extends FooService with FooRemoting with FooPersistence

Nun, wenn FooRemoting und FooPersistence beide von FooService geerbt hätte, und FooService hat Mitglieder und Methoden, wie würden Dienstleistungen aussehen?

Während für die Vererbung, würden wir so etwas wie haben:

trait Iterator[T] {
  def hasNext : boolean
  def next : T
}

trait InfiniteIterator[T] extends Iterator[T] {
  def hasNext = true
}

Andere Tipps

Selbst Typenannotationen können Sie zyklische Abhängigkeiten auszudrücken. Zum Beispiel:

trait A extends B
trait B { self: A => }

Dies ist nicht möglich, mit einfacher Vererbung.

Da die Frage zu stellen ich auf diesem Posten kam:

Spiros Tzavellas Gespräche über eine Eigenschaft als öffentliche Schnittstelle und den Selbsttyp als Helfer verwendet, die durch die Implementierungsklasse gemischt werden müssen.

  

Zum Schluss, wenn wir bewegen wollen   Verfahren Implementierungen innerhalb Züge   dann laufen wir Gefahr, die Schnittstelle verschmutzen   diese Züge mit abstrakten Methoden   dass Unterstützung bei der Durchführung der   konkrete Methoden und sind unabhängig   mit der Hauptverantwortung für die   Merkmal. Eine Lösung für dieses Problem ist,   diese abstrakten Methoden zu bewegen, in   andere Merkmale und komponieren die Züge   Verwendung zusammen Selbst Typenannotationen   und Mehrfachvererbung.

Zum Beispiel:

trait PublicInterface { this: HelperTrait =>
  // Uses helperMethod
}

trait HelperTrait {
  def helperMethod = // ...
}

class ImplementationClass extends PublicInterface with HelperTrait

A Tour of Scala mit Typ selbst bespricht Anmerkungen mit abstrakten Typ-Mitglieder - vermutlich ist es nicht möglich, ein abstraktes Typ Element (?)

extend

Die Antwort ist „Zirkularität“. Aber nicht nur.

Selbst Typenannotation löst für mich das grundlegende Problem der Vererbung: was Sie erben von nicht verwenden können, was Sie sind. Mit dem Selbst Typ, wird alles einfach.

Ihr Muster ist die folgende und kann als degenerierten Kuchen in Betracht gezogen werden:

trait A { self: X => def a = reuseme}
trait B { self: X => def b = a }
class X extends A with B { def reuseme=null }

Sie können Ihre Klasse in mehreren Verhaltensweisen explodieren, die von überall in der Versammlung aufgerufen werden können, während sauber getippt bleiben. Keine Notwendigkeit für die schmerzhafte indirection zu oft (und falsch) mit dem Kuchen Muster identifiziert.

Die Hälfte (wenn nicht alle) der gewundenen Java DI Frameworks der letzten 10 Jahre gewidmet haben, dies zu tun, natürlich ohne die Typisierung. Die Menschen in dieser Domäne zu verlieren mit JAVA noch deutlich ihre Zeit. „SCALA ouakbar“

Ich weiß, diese Frage ist alt, aber ich möchte eine Klarstellung und Beispiele hinzuzufügen.

Es gibt drei Hauptunterschiede zwischen Charakterzug Vererbung und Selbsttypen.

Beschreibung

Die Vererbung ist eine der Beziehungen mit der meisten Kopplung des Objekt Paradigma, wenn A B erstreckt, das bedeutet, daß A ein B.

Lassen Sie uns sagen, dass wir den folgenden Code haben,

trait Animal {
  def stop():Unit = println("stop moving")
}

class Dog extends Animal {
  def bark:String = "Woof!"
}

val goodboy:Dog = new Dog

goodboy.bark
// Woof!

Wir sagen, dass ein Hund ist ein Tier. Wir können die Nachrichten bark und stop senden, weil goodboy ist ein Hund, es beiden Methoden verstehen.

Nehmen wir nun an haben wir ein neues Merkmal haben,

trait Security {
  this: Animal =>
  def lookout:Unit = { stop(); println("looking out!") }
}

Dieses Mal Sicherheit ist kein Tier, und das ist in Ordnung, weil semantisch falsch wäre, wenn wir behaupten, dass eine Sicherheit ein Tier ist, sind sie unterschiedliche Konzepte, kann diese zusammen verwendet werden.

So, jetzt können wir eine neue Art von Hund erstellen,

val guardDog = new Dog with Security

guardDog.lookout
// stop moving
// looking out!

guardDog ist ein Hund, ein Tier und Sicherheit. Es versteht stop, bark und lookout weil ein Hund mit Sicherheit ist.

Aber was passiert, wenn wir einen neuen Hund so?

erstellen
val guardDog2:Dog = new Dog with Security
guardDog2.lookout // no such method!

guardDog2 ist nur ein Hund, so können wir nicht lookout Methode aufrufen. (Okok, es ist ein Hund mit Sicherheit, aber wir sehen nur einen Hund)

Zyklische Abhängigkeiten

Self-Typen ermöglichen es uns, zyklische Abhängigkeiten zwischen verschiedenen Arten zu erstellen.

trait Patient {
  this: Reader =>
  def isQuite:Boolean = isReading
  def isSlow:Boolean = true
}

trait Reader {
  this: Patient =>
  def read():Unit = if(isSlow) println("Reading Slow...") else println("Reading Fast...")
  def isReading = true
}

val person = new Patient with Reader

Der folgende Code lässt sich nicht kompilieren.

trait Patient extends Reader { /** code **/}

trait Reader extends Patient { /** code **/ }

Diese Art von Code ist in Dependency Injection (Kuchen-Muster) sehr verbreitet.

Vielseitigkeit

Last but not least, die unsere Züge verwendet, kann die Reihenfolge entscheiden, in dem sie verwendet werden, so dank Trait Arisierung das Endergebnis unterschiedlich sein können, obwohl die verwendeten Merkmale gleich sind.

Mit normaler Vererbung wir das nicht tun können, werden die Beziehungen zwischen Eigenschaften und Klassen festgelegt.

trait Human {
  def isGoodForSports:Boolean
}

trait Programmer extends Human {
  def readStackOverflow():Unit = println("Reading...")
  override def isGoodForSports: Boolean = false
}

trait Sportsman extends Human {
  def play():Unit = println("Playing something")
  override def isGoodForSports: Boolean = true
}

val foo = new Programmer with Sportsman
foo.isGoodForSports
// true

val bar = new Sportsman with Programmer
bar.isGoodForSports
// false

Hope dies nützlich sein kann.

Auch wenn es nicht Ihre Frage nicht beantworten, ich habe versucht, die Selbst Typenannotationen zu verstehen, hätte im Grunde in Antworten verloren, und am Ende irgendwie auf dem Fahrrad durch Variationen Ihrer Frage, die auf Verwendung von Annotationen selbstTyp konzentriert sich für Angabe Abhängigkeiten.

Also hier schreibe ich eine Beschreibung einer Verwendung Fall, in dem selbst Typenannotationen gut bebildert sind, nämlich so etwas wie ein typsicher Fall von ‚this‘ als Subtyp:

http://programming-scala.labs.oreilly.com/ch13. html # SelfTypeAnnotationsAndAbstractTypeMembers

Hoffnung, dass es zufällig zu denen, die auf dieser Frage am Ende wäre hilfreich (und, wie ich, hatte keine Zeit, ein scala Buch vor dem Start zu erkunden :-) zu lesen)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top