Herencia Múltiple: jugando a ser Dios

Una de las características de Scala más importantes es la posibilidad de hacer mixin. Mediante mixin podemos tener herencia múltiple de varios traits. ¿Traits? ¿Mixin? ¿Lo qué? Bueno, vamos a empezar poco a poco 🙂

¿Qué son los traits?

 
Los Traits (o rasgos en la lengua de Cervantes) son un aglutinamiento de atributos y métodos propios de un rasgo o característica. Son similares a las interfaces de Java con la salvedad de que se pueden implementar parcialmente, es decir, solo algunos de sus métodos o valores.

Sirve para añadir comportamientos a otras entidades. Por ejemplo, vamos a implementar el trait CanFly en el que definiremos los valores y métodos necesarios para cualquier criatura voladora:


trait CanFly {

  val altitude: Float

  def fly() = println("I'm flying!!!")
}

Como se puede observar, en este trait existe una variable altitude que está sin definir, y un método fly que sí está definido. Podemos instanciar un trait utilizando una clase anónima de la siguiente forma:


val myFlyingAnimal = new CanFly {}

Sin embargo, como la variable altitude no está definida, este trozo de código provocará un error de compilación. Para poder instanciar un trait, es necesario implementar todos sus valores y métodos:


val myFlyingAnimal = new CanFly { val altitude = 100.0f }

 

¿Cómo podemos utilizar los traits?

 
A pesar de que ya sabemos como instanciar traits mediante clases anónimas, no es uno de sus usos más comunes.

Los traits se usan para añadir comportamientos a nuestras entidades. Para ello podemos, mediante composición, hacer mixin de uno o varios traits a la hora de definir otro trait, una clase, una case class …

Para ello, se utilizan dos palabras reservadas: extends y with. El primer trait tendrá como predecesor la palabra extends, el resto se irán incluyendo con with.


case class Pigeon extends CanFly

case class Superman extends Superhero with CanFly

 

nlY1Wxv

A esto es a lo que llamamos Mixin. ¿A que ahora todo tiene más sentido?

¿Y cómo trabajan los mixin con el problema del diamante?

 
Primero vamos a crear un escenario con el problema. Tenemos un trait Animal que tiene un método sin implementar que devuelve el sonido del animal.


trait Animal {
  def sound: String
}

Por otro lado, tenemos dos especificaciones del animal: un gato y un perro.


trait Cat extends Animal {
  override def sound = "Miau"
}

trait Dog extends Animal {
  override def sound = "Guau"
}

Haciendo un esfuerzo de imaginación sin igual, vamos a imaginar que un zorro es la mezcla entre un gato y un perro.


case class Fox extends Cat with Dog

Pero, si intentamos imprimir el sonido del zorro….


val myFox = Fox()
println(myFox.sound)

 

nlY1Wxv

 

Pues en este caso, nuestro zorro ladrará. ¿Por qué? Por que el último trait con el que se ha hecho mixin es Dog. Primer se utiliza la implementación de Cat, pero más tarde es sobrescrita por el siguiente trait definido, es decir, Dog. Por tanto podemos decir, que los traits se van instanciando en el orden en el que se han declarado.

¡Y ya está! ¿Qué hemos aprendido hoy? Que los traits y los mixins son bastante útiles….y que los zorros, en ocasiones, ladran.

Un comentario en “Herencia Múltiple: jugando a ser Dios

Deja un comentario