Scala Language
Züge
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,
First
Shape
wird linearisiert, was wie folgt aussieht:Shape -> AnyRef -> Any
Dann wird
Dotted
linearisiert:Dotted -> Border -> Shape -> AnyRef -> Any
Als nächstes kommt
Blue
. Normalerweise ist die Linearisierung vonBlue
:Blue -> Color -> Shape -> AnyRef -> Any
denn in
MyShape
Linearisierung ( Schritt 2 ) istShape -> AnyRef -> Any
bereits erschienen. Daher wird es ignoriert. DieBlue
Linearisierung ist also:Blue -> Color -> Dotted -> Border -> Shape -> AnyRef -> Any
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 .