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,

  1. La première Shape sera linéarisée, ce qui ressemble à:

    Shape -> AnyRef -> Any

  2. Puis Dotted est linéarisé:

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

  3. Le suivant est le Blue . Normalement, la linéarisation de Blue 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éarisation Blue sera:

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

  4. 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 .



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow