Suche…


Syntax

  • Merkmal ATrait {...}
  • Klasse AClass (...) erweitert ATrait {...}
  • Klasse AClass erweitert BClass mit ATrait
  • Klasse AClass erweitert ATrait um BTrait
  • Klasse AClass erweitert ATrait um BTrait um CTrait
  • Klasse ATrait erweitert BTrait

Stapelbare Modifikation mit Eigenschaften

Sie können Merkmale verwenden, um die Methoden einer Klasse zu ändern, indem Sie die Merkmale stapelbar verwenden.

Das folgende Beispiel zeigt, wie Merkmale aufeinander gestapelt werden können. Die Reihenfolge der Merkmale ist wichtig. Durch die unterschiedliche Reihenfolge der Merkmale wird ein unterschiedliches Verhalten erreicht.

class Ball {
  def roll(ball : String) = println("Rolling : " + ball)
}

trait Red extends Ball {
  override def roll(ball : String) = super.roll("Red-" + ball)
}

trait Green extends Ball {
  override def roll(ball : String) = super.roll("Green-" + ball)
}

trait Shiny extends Ball {
  override def roll(ball : String) = super.roll("Shiny-" + ball)
}

object Balls {
  def main(args: Array[String]) {
    val ball1 = new Ball with Shiny with Red
    ball1.roll("Ball-1") // Rolling : Shiny-Red-Ball-1

    val ball2 = new Ball with Green with Shiny
    ball2.roll("Ball-2") // Rolling : Green-Shiny-Ball-2
  }
}

Beachten Sie, dass super zum Aufrufen der roll() -Methode in beiden Merkmalen verwendet wird. Nur so können wir eine stapelbare Modifikation erreichen. Bei stapelbaren Modifikationen wird die Reihenfolge der Methodenaufrufe durch eine Linearisierungsregel bestimmt .

Trait-Grundlagen

Dies ist die grundlegendste Version eines Merkmals in Scala.

trait Identifiable {
  def getIdentifier: String
  def printIndentification(): Unit = println(getIdentifier)
}

case class Puppy(id: String, name: String) extends Identifiable {
  def getIdentifier: String = s"$name has id $id"
}

Da keine übergeordnete Klasse für die Eigenschaft Identifiable deklariert ist, geht sie standardmäßig von der AnyRef Klasse aus. Da in Identifiable keine Definition für getIdentifier ist, muss die Puppy Klasse diese implementieren. Puppy printIdentification die Implementierung von printIdentification von Identifiable .

In der REPL:

val p = new Puppy("K9", "Rex")
p.getIdentifier  // res0: String = Rex has id K9
p.printIndentification()  // Rex has id K9

Das Diamantproblem lösen

Das Raute-Problem oder die Mehrfachvererbung wird von Scala mithilfe von Traits behandelt, die Java-Schnittstellen ähneln. Traits sind flexibler als Schnittstellen und können implementierte Methoden enthalten. Dies macht Merkmale ähnlich wie Mixins in anderen Sprachen.

Scala unterstützt keine Vererbung von mehreren Klassen. Ein Benutzer kann jedoch mehrere Merkmale in einer einzigen Klasse erweitern:

trait traitA {
  def name = println("This is the 'grandparent' trait.")
}

trait traitB extends traitA {
  override def name = {
    println("B is a child of A.")
    super.name
  }

}

trait traitC extends traitA {
  override def name = {
    println("C is a child of A.")
    super.name
  }
}

object grandChild extends traitB with traitC

grandChild.name

Hier grandChild erbt von beiden traitB und traitC , die wiederum beide von erben traitA . Die Ausgabe (unten) zeigt auch die Rangfolge bei der Auflösung, welche Methodenimplementierungen zuerst aufgerufen werden:

C is a child of A. 
B is a child of A. 
This is the 'grandparent' trait.

Beachten Sie, dass bei der Verwendung von super zum Aufrufen von Methoden in class oder trait Linearisierungsregel zur Entscheidung der Aufrufhierarchie ins Spiel kommt. Die Linearisierungsreihenfolge für grandChild :

grandChild -> traitC -> traitB -> traitA -> AnyRef -> Any


Unten ist ein anderes Beispiel:

trait Printer {
  def print(msg : String) = println (msg)
}

trait DelimitWithHyphen extends Printer {
  override def print(msg : String) {
    println("-------------")
    super.print(msg)
  }
}

trait DelimitWithStar extends Printer  {
  override def print(msg : String) {
    println("*************")
    super.print(msg)
  }
}

class CustomPrinter extends Printer with DelimitWithHyphen with DelimitWithStar

object TestPrinter{
  def main(args: Array[String]) {
    new CustomPrinter().print("Hello World!")
  }
}

Dieses Programm druckt:

*************
-------------
Hello World!

Die Linearisierung für CustomPrinter :

CustomPrinter -> DelimitWithStar -> DelimitWithHyphen -> Drucker -> AnyRef -> Any

Linearisierung

Bei stapelbaren Modifikationen ordnet Scala Klassen und Merkmale in linearer Reihenfolge an, um die Methodenaufrufhierarchie zu bestimmen, die als Linearisierung bezeichnet wird . Die Linearisierungsregel wird nur für Methoden verwendet, die den Methodenaufruf über super() erfordern. Betrachten wir dies anhand eines Beispiels:

class Shape {
  def paint (shape: String): Unit = {
    println(shape)
  }
}

trait Color extends Shape {
  abstract override def paint (shape : String) {
    super.paint(shape + "Color ")
  }
}

trait Blue extends Color {
  abstract override def paint (shape : String) {
    super.paint(shape + "with Blue ")
  }
}

trait Border extends Shape {
  abstract override def paint (shape : String) {
    super.paint(shape + "Border ")
  }
}

trait Dotted extends Border {
  abstract override def paint (shape : String) {
    super.paint(shape + "with Dotted ")
  }
}

class MyShape extends Shape with Dotted with Blue {
  override def paint (shape : String) {
    super.paint(shape)
  }
}

Die Linearisierung erfolgt von hinten nach vorne . In diesem Fall,

  1. First Shape wird linearisiert, was wie folgt aussieht:

    Shape -> AnyRef -> Any

  2. Dann wird Dotted linearisiert:

    Dotted -> Border -> Shape -> AnyRef -> Any

  3. Als nächstes kommt Blue . Normalerweise ist die Linearisierung von Blue :

    Blue -> Color -> Shape -> AnyRef -> Any

    denn in MyShape Linearisierung ( Schritt 2 ) ist Shape -> AnyRef -> Any bereits erschienen. Daher wird es ignoriert. Die Blue Linearisierung ist also:

    Blue -> Color -> Dotted -> Border -> Shape -> AnyRef -> Any

  4. Schließlich wird Circle hinzugefügt und die endgültige Linearisierungsreihenfolge lautet:

    Kreis -> Blau -> Farbe -> Punkt -> Rand -> Form -> AnyRef -> Any

Diese Linearisierungsreihenfolge bestimmt die Aufrufreihenfolge von Methoden, wenn super in einer Klasse oder einem Merkmal verwendet wird. Die erste Methodenimplementierung von rechts wird in der Linearisierungsreihenfolge aufgerufen. Wenn new MyShape().paint("Circle ") ausgeführt wird, wird new MyShape().paint("Circle ") gedruckt:

Circle with Blue Color with Dotted Border 

Weitere Informationen zur Linearisierung finden Sie hier .



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow