En este capitulo vamos a ver como se trabaja con traits. Los traits
son similares a las interfaces de java solo que estas puede proveer
estados y comportamiento, lo que las hace más útiles.
Una clase puede implementar muchos traits
Un traits puede requerir implementar campos, métodos o súper clases
A diferencia de Java, los traits pueden implementar métodos o
campos.
Cuando se superponen varios traits, el orden es importante: el trait
cuyos métodos se ejecutan primero van en la parte posterior.
Por que no herencia múltiple?
Scala al igual que Java no permite que una clase herede de varias
clases, esto al parecer es una restricción muy grande. Porque no
debemos extender de múltiples clases. Si por ejemplo lenguajes como
C++ lo permiten.
La herencia múltiple trabaja muy bien cuando se combinan clases que
no tienen nada en común, pero cuando combinamos clases que tienen
métodos en común se presentan diferentes problemas.
Veamos un ejemplo un asistente del profesor o alumno auxiliar :
class Student {
def id: String = …
…
}
class Employee {
def id: String = …
...
}
class TeachingAssistant extends Student, Employee { // Ojo que esto
no compila en scala
…
}
Desafortunadamente TeachingAssistant tiene 2 métodos id, por lo
tanto que debe hacer cuando llamamos a myTA.id, siendo myTA una
instancia de TeachingAssistant. (en c++ necesitamos redefinir este
método para clarificar la situación)
Ahora supongamos que tanto el empleado como el estudiante heredan de
persona:
class Person {
var name: String = _
}
class Student extends Person { ... }
class Employee extends Person { ... }
Esto se denomina el problema del diamante, ahora TeachingAssistant
tiene 2 campos nombre pero nosotros solo queremos uno, como se
debería combinar?
En Java se decidió un camino más restrictivo para resolver estos
problemas, no se permite herencia múltiple de clases pero se invento
el concepto de interfaz que no permite que se implementen métodos y
no puede contener atributos pero puede ser heredado de forma
múltiple.
Scala no permite herencia múltiple pero tiene los traits o rasgos.
Un trait puede contener métodos abstractos o concretos y estados y
una clase puede implementar múltiples traits. Ahora veremos como
scala resuelve los conflictos de implementación.
Traits como interfaces Java
Los traits puede ser utilizadas de igual manera que las interfaces de
Java:
trait Logger {
def log(msg: String) // Método abstracto
}
No es necesario indicar que este método es abstracto, si no se
implementa en un trait es automáticamente abstracto.
Y una clase puede proveer una implementación:
class ConsoleLogger extends Logger { // Se usa extends y no
implements
def log(msg: String) { println(msg) } // No es necesario el
override
}
No es necesario agregar override cuando se sobrescribir un método
abstracto de un trait.
Si es necesario agregar más interfaces, se debe hacerlo con la
palabra clave with :
class ConsoleLogger extends Logger with Cloneable with Serializable
Como se puede ver se están utilizando interfaces Cloneable y
Serializable que son de Java, sin ningún problema.