Sobre escribir métodos abstractos en Traits
Del ejemplo anterior, podemos deducir que TimestampLogger y
ShortLogger extienden de ConsoleLogger, la cual extiende del trait
Logger el cual no tiene implementación para el método log
trait Logger {
def log(msg: String) // This method is abstract
}
Por lo tanto
trait TimestampLogger extends Logger {
override def log(msg: String) { // Overrides an abstract method
super.log(s"${java.time.Instant.now()} $msg") // Is
super.log defined?
}
}
Esto no debería compilar porque super.log no fue definido pero scala
lo toma como que la clase TimestampLogger es abstracta y solo se
puede utilizar si se mixea con una clase que implemente este método
Traits para interfaces ricas.
Un trait puede tener muchos métodos útiles que dependan de unos pocos abstractos. Un ejemplo de esto es el Iterator que define docenas de métodos en términos de los métodos abstractos next y hasnext. Vamos a construir métodos que nos permitan imprimir diferentes tipos de logs
trait Logger {
def log(msg: String)
def info(msg: String) { log(s"INFO: $msg") }
def warn(msg: String) { log(s"WARN: $msg") }
def severe(msg: String) { log(s"SEVERE: $msg") }
}
Note que estamos combinando métodos abstractos con implementaciones concretas.
abstract class SavingsAccount extends Account with Logger {
def withdraw(amount: Double) {
if (amount > balance) severe("Fondos insuficientes")
else …
}
…
}
Un campo concreto en Traits
Un campo de un traits puede ser concreto o abstracto. Si se asigna un valor al campo este será concreto,
trait ShortLogger extends Logger {
val maxLength = 15 // A concrete field
abstract override def log(msg: String) {
super.log(
if (msg.length <= maxLength) msg
else s"${msg.substring(0, maxLength – 3)}...")
}
}
Una clase puede mixear con el campo maxLenght
class SavingsAccount extends Account with ConsoleLogger with
ShortLogger {
var interest = 0.0
def withdraw(amount: Double) {
if (amount > balance) log("Fondos insuficientes")
else …
}
}
Note que la clase SavingsAccount tiene la variable interest, pero no
balance, balance viene de Account
class Account {
var balance = 0.0
}
La maquina virtual java no permite heredar campos de diferentes interfaces o super clases, por lo que el compilador scala debe copiarlos.
Campos abstractos en Traits
Los campos que no son inicializados son abstractos, y deben ser sobreescritos por la subclase concreta.
Por ejemplo:
trait ShortLogger extends Logger {
val maxLength: Int // An abstract field
abstract override def log(msg: String) { ... }
super.log(
if (msg.length <= maxLength) msg
else s"${msg.substring(0, maxLength – 3)}...")
// El campo maxLength es utilizado en la implementación.
}
Cuando se utiliza este trait se debe asignar un valor a maxLength:
class SavingsAccount extends Account with ConsoleLogger with ShortLogger {
val maxLength = 20 // No es necesario escribir override
}
Ahora todos los mensajes de registro se truncan después de 20 caracteres.
Esta forma de proporcionar valores para los parámetros de rasgo es particularmente útil cuando se construyen objetos sobre la marcha. Volvamos a nuestra cuenta de ahorros original:
class SavingsAccount extends Account with Logger { ... }
Ahora, podemos truncar los mensajes en una instancia de la siguiente manera:
val acct = new SavingsAccount with ConsoleLogger with ShortLogger {
val maxLength = 20
}