Buscar..


Sintaxis

  • rasgo ATrait {...}
  • clase AClass (...) extiende ATrait {...}
  • clase AClass extiende BClass con ATrait
  • clase AClass extiende ATrait con BTrait
  • La clase AClass extiende ATrait con BTrait con CTrait.
  • clase ATrait extiende BTrait

Modificación apilable con rasgos

Puede usar rasgos para modificar los métodos de una clase, usando rasgos en forma apilable.

El siguiente ejemplo muestra cómo se pueden apilar los rasgos. El ordenamiento de los rasgos es importante. Usando diferentes orden de rasgos, se logra un comportamiento diferente.

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
  }
}

Tenga en cuenta que super se utiliza para invocar el método roll() en ambos rasgos. Solo así podremos lograr una modificación apilable. En casos de modificación apilable, el orden de invocación del método está determinado por la regla de linealización .

Fundamentos del rasgo

Esta es la versión más básica de un rasgo en 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"
}

Dado que no se declara ninguna AnyRef para el carácter Identifiable , por defecto se extiende desde la clase AnyRef . Debido a que no se proporciona una definición para getIdentifier en Identifiable , la clase Puppy debe implementarla. Sin embargo, Puppy hereda la implementación de printIdentification de Identifiable .

En el REPL:

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

Resolviendo el problema del diamante

El problema de diamante , o herencia múltiple, es manejado por Scala usando Rasgos, que son similares a las interfaces de Java. Los rasgos son más flexibles que las interfaces y pueden incluir métodos implementados. Esto hace rasgos similares a mixins en otros idiomas.

Scala no admite la herencia de varias clases, pero un usuario puede extender múltiples rasgos en una sola clase:

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

Aquí grandChild se hereda de traitB y traitC , que a su vez heredan de traitA . La salida (a continuación) también muestra el orden de prioridad al resolver qué implementaciones de métodos se llaman primero:

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

Tenga en cuenta que, cuando se utiliza super para invocar métodos en class o trait , la regla de linealización entra en juego para decidir la jerarquía de llamadas. El orden de linealización para grandChild será:

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


A continuación se muestra otro ejemplo:

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!")
  }
}

Este programa imprime:

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

La linealización para CustomPrinter será:

CustomPrinter -> DelimitWithStar -> DelimitWithHyphen -> Printer -> AnyRef -> Any

Linealización

En caso de modificación apilable , Scala organiza clases y rasgos en un orden lineal para determinar la jerarquía de llamadas a métodos, lo que se conoce como linealización . La regla de linealización se usa solo para aquellos métodos que involucran la invocación de métodos a través de super() . Consideremos esto con un ejemplo:

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 linealización pasa de atrás hacia delante . En este caso,

  1. La primera Shape será linealizada, lo que se parece a:

    Shape -> AnyRef -> Any

  2. Entonces Dotted se linealiza:

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

  3. El siguiente en la línea es Blue . Normalmente la linealización del Blue será:

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

    porque, en la linealización de MyShape hasta ahora ( Paso 2 ), Shape -> AnyRef -> Any ya ha aparecido. Por lo tanto, se ignora. Así, la linealización Blue será:

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

  4. Finalmente, se agregará el Circle y el orden de linealización final será:

    Círculo -> Azul -> Color -> Punteado -> Borde -> Forma -> AnyRef -> Cualquiera

Este orden de linealización decide el orden de invocación de los métodos cuando se utiliza super en cualquier clase o rasgo. La primera implementación del método desde la derecha se invoca, en el orden de linealización. Si se new MyShape().paint("Circle ") , se imprimirá:

Circle with Blue Color with Dotted Border 

Más información sobre la linealización se puede encontrar aquí .



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow