Scala Language
Traits
Recherche…
Syntaxe
- trait ATrait {...}
- classe AClass (...) étend ATrait {...}
- classe AClass étend BClass avec ATrait
- classe AClass étend ATrait avec BTrait
- classe AClass étend ATrait avec BTrait avec CTrait
- classe ATrait étend BTrait
Modification empilable avec des traits
Vous pouvez utiliser des traits pour modifier les méthodes d'une classe, en utilisant des traits empilables.
L'exemple suivant montre comment les traits peuvent être empilés. L'ordre des traits est important. En utilisant un ordre différent de traits, un comportement différent est obtenu.
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
}
}
Notez que super
est utilisé pour invoquer la méthode roll()
dans les deux traits. Ce n’est qu’ainsi que nous pourrons réaliser des modifications empilables. En cas de modification empilable, l'ordre d'invocation de la méthode est déterminé par la règle de linéarisation .
Les bases du trait
C'est la version la plus basique d'un trait de 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"
}
Comme aucune super classe n'est déclarée pour trait Identifiable
, elle s'étend par défaut de la classe AnyRef
. Comme aucune définition de getIdentifier
n'est fournie dans Identifiable
, la classe Puppy
doit l'implémenter. Cependant, Puppy
hérite de l'implémentation de printIdentification
d' Identifiable
.
Dans le REPL:
val p = new Puppy("K9", "Rex")
p.getIdentifier // res0: String = Rex has id K9
p.printIndentification() // Rex has id K9
Résoudre le problème du diamant
Le problème des diamants , ou héritage multiple, est géré par Scala à l'aide de Traits, qui sont similaires aux interfaces Java. Les caractéristiques sont plus flexibles que les interfaces et peuvent inclure des méthodes implémentées. Cela rend les traits similaires aux mixins dans d'autres langues.
Scala ne prend pas en charge l'héritage de plusieurs classes, mais un utilisateur peut étendre plusieurs caractéristiques dans une seule classe:
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
Ici, grandChild
hérite à la fois de traitB
et de traitC
, qui héritent à leur tour de traitA
. La sortie (ci-dessous) montre également l'ordre de priorité lors de la résolution des implémentations de méthode appelées en premier:
C is a child of A.
B is a child of A.
This is the 'grandparent' trait.
Notez que lorsque super
est utilisé pour invoquer des méthodes en class
ou en trait
, la règle de linéarisation intervient pour décider de la hiérarchie des appels. L'ordre de linéarisation pour grandChild
sera:
grandChild -> traitC -> traitB -> traitA -> AnyRef -> Tous
Voici un autre exemple:
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!")
}
}
Ce programme imprime:
*************
-------------
Hello World!
La linéarisation de CustomPrinter
sera:
CustomPrinter -> DelimitWithStar -> DelimitWithHyphen -> Imprimante -> AnyRef -> N'importe quel
Linéarisation
En cas de modification empilable , Scala organise les classes et les traits dans un ordre linéaire pour déterminer la hiérarchie des appels de méthode, appelée linéarisation . La règle de linéarisation est utilisée uniquement pour les méthodes impliquant un appel de méthode via super()
. Considérons ceci par un exemple:
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)
}
}
La linéarisation se produit de l' arrière vers l'avant . Dans ce cas,
La première
Shape
sera linéarisée, ce qui ressemble à:Shape -> AnyRef -> Any
Puis
Dotted
est linéarisé:Dotted -> Border -> Shape -> AnyRef -> Any
Le suivant est le
Blue
. Normalement, la linéarisation deBlue
sera:Blue -> Color -> Shape -> AnyRef -> Any
car, dans la linéarisation de
MyShape
jusqu'à maintenant ( étape 2 ),Shape -> AnyRef -> Any
est déjà apparu. Par conséquent, il est ignoré. Ainsi, la linéarisationBlue
sera:Blue -> Color -> Dotted -> Border -> Shape -> AnyRef -> Any
Enfin,
Circle
sera ajouté et l’ordre de linéarisation final sera:Cercle -> Bleu -> Couleur -> Pointillé -> Bordure -> Forme -> AnyRef -> N'importe quel
Cet ordre de linéarisation détermine l'ordre d'invocation des méthodes lorsque super
est utilisé dans une classe ou un trait. La première implémentation de la méthode à droite est appelée dans l'ordre de linéarisation. Si new MyShape().paint("Circle ")
est exécuté, il imprimera:
Circle with Blue Color with Dotted Border
Vous trouverez plus d'informations sur la linéarisation ici .