compareTo y equals del tipo de id envuelto (e.g. String)import java.util.*;
import java.io.*;
public final class BankAccount implements Comparable<BankAccount> {
private final String id;
public BankAccount (String number) {
this.id = number;
}
public String getId() { return id; }
@Override
public int compareTo(BankAccount other) {
if (this == other) return 0;
assert this.equals(other) : "compareTo inconsistent with equals.";
return this.id.compareTo(other.getId());
}
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (!(other instanceof BankAccount)) return false;
BankAccount that = (BankAccount)other;
return this.id.equals(that.getId());
}
@Override
public String toString() {
return id.toString();
}
}
Object. Hay que hacer casting.Boolean que no implementa Comparable en JDK 1.4import java.util.*;
import java.io.*;
public final class BankAccount implements Comparable {
private final String id;
public BankAccount (String number) {
this.id = number;
}
public String getId() { return id; }
public int compareTo(Object other) {
if (this == other) return 0;
assert (other instanceof BankAccount) : "compareTo comparing objects of different type";
BankAccount that = (BankAccount)other;
assert this.equals(that) : "compareTo inconsistent with equals.";
return this.id.compareTo(that.getId());
}
public boolean equals(Object other) {
if (this == other) return true;
if (!(other instanceof BankAccount)) return false;
BankAccount that = (BankAccount)other;
return this.id.equals(that.getId());
}
public String toString() {
return id.toString();
}
}
Cuando una clase hereda de una clase concreta que implementa Comparable y le añade un campo significativo para la comparación, no se puede construir una implementación correcta de compareTo. La única alternativa entonces es la composición en lugar de la herencia.
Una alternativa (no excluyente) a implementar Comparable es pasar un Comparator como parámetro (se prefiere composición frente a herencia):
BankAccount implementa Comparable:class BankAccountComparator implements java.util.Comparator<BankAccount> {
public int compare(BankAccount o1, BankAccount o2) {
return o1.compareTo(o2);
}
}
BankAccount no implementa Comparable:class BankAccountComparator implements java.util.Comparator<BankAccount> {
public int compare(BankAccount o1, BankAccount o2) {
return compare(o1.getId(), o2.getId());
}
}
En Scala se puede implementar el equivalente a la interfaz Comparable de Java mediante traits:
object MiApp {
def main(args: Array[String]) : Unit = {
val f1 = new Fecha(12,4,2009)
val f2 = new Fecha(12,4,2019)
println(s"$f1 es posterior a $f2? ${f1>=f2}")
}
}
trait Ord {
def < (that: Any): Boolean
def <=(that: Any): Boolean = (this < that) || (this == that)
def > (that: Any): Boolean = !(this <= that)
def >=(that: Any): Boolean = !(this < that)
}
class Fecha(d: Int, m: Int, a: Int) extends Ord {
def anno = a
def mes = m
def dia = d
override def toString(): String = s"$dia-$mes-$anno"
override def equals(that: Any): Boolean =
that.isInstanceOf[Fecha] && {
val o = that.asInstanceOf[Fecha]
o.dia == dia && o.mes == mes && o.anno == anno
}
def <(that: Any): Boolean = {
if (!that.isInstanceOf[Fecha])
sys.error("no se puede comparar" + that + " y una fecha")
val o = that.asInstanceOf[Fecha]
(anno < o.anno) ||
(anno == o.anno && (mes < o.mes ||
(mes == o.mes && dia < o.dia)))
}
}
Un mixin es un módulo/clase con métodos disponibles para otros módulos/clases sin tener que usar la herencia
¿Qué lenguajes tienen mixins?
En Ruby los mixins se implementan mediante módulos (module).
include) dentro de la definición de una claseUna manera de implementar un Comparable en ruby mediante el módulo Comparable:
La clase que incluye el módulo Comparable tiene que implementar:
<=>: es un método que incluye los siguientes operadores/métodos: <, <=, ==, >, >=, between?En x <=> y, x es el receptor del mensaje/método e y es el argumento
class Student
include Comparable
attr_accessor :name, :score
def initialize(name, score)
@name = name
@score = score
end
def <=>(other)
@score <=> other.score
end
end
s1 = Student.new("Peter", 100)
s2 = Student.new("Jason", 90)
s3 = Student.new("Maria", 95)
s1 > s2 #true
s1 <= s2 #false
s3.between?(s1,s2) #true
Un trait es una forma de separar las dos principales responsabilidades de una clase: definir el estado de sus instancias y definir su comportamiento.
Las clases y los objetos en Scala pueden extender un trait
Los traitde Scala son similares a las interface de Java.
Los trait no pueden instanciarse
Los métodos definidos en una clase tienen precedencia sobre los de un trait
Los trait no tienen estado propio, sino el del objeto o la instancia de la clase a la que se aplica
trait Iterator[A] {
def hasNext: Boolean
def next(): A
}
class IntIterator(to: Int) extends Iterator[Int] {
private var current = 0
override def hasNext: Boolean = current < to
override def next(): Int = {
if (hasNext) {
val t = current
current += 1
t
} else 0
}
}
val iterator = new IntIterator(10)
println(iterator.next()) // prints 0
println(iterator.next()) // prints 1
¿Un trait de Scala es un mixin?
trait Fighter {
def fight(): String //abstract
}
trait Flyer {
def startFlying(): Unit = println("start flying")
def stopFlying(): Unit = println("stop flying")
}
trait Swimmer {
def startSwimming(): Unit = println("start swimming")
def stopSwimming(): Unit = println("stop swimming")
}
class Hero(name: String) extends Fighter with Flyer {
def fight(): String = "thump!"
}
class AmphibiousHero extends Fighter with Flyer with Swimmer {
def fight(): String = "splash!"
}
object Test {
def main(args: Array[String]): Unit = {
val superman = new Hero("Superman")
val aquawoman = new AmphibiousHero
println( superman.fight() )
superman.startFlying()
println( aquawoman.fight() )
aquawoman.startSwimming()
val aquaman = new Hero("Aquaman") with Swimmer
aquaman.startSwimming()
}
}
Los traits de Scala tienen una interfaz que las clases heredan (extends)
Entonces... una clase que extiende un trait con un comportamiento, ¿va contra el principio general de que la herencia de comportamiento es una mala idea?
Lectura recomendada: Scala Mixins: The right way
¿Y en Java no hay traits?
defaultResolver la ambigüedad en la herencia múltiple con métodos default
interface Volador {
default void mover() {
System.out.println("Moviendo por aire");
}
}
interface Nadador {
default void mover() {
System.out.println("Moviendo por agua");
}
}
class Pato implements Volador, Nadador {
@Override
public void mover() {
// Obligatorio resolver el conflicto
Volador.super.mover(); // o Nadador.super.mover()
System.out.println("... como un pato");
}
}
// Válido desde JDK 25...
void main() {
var pato = new Pato();
pato.mover();
}
¿Qué ventajas tienen las implementaciones basadas en Composición frente a las basadas en Herencia (estática)?
La respuesta está en la inyección de dependencias...
Críticas acumuladas aplicables a la v0.1 para llegar a la implementación final v0.7
Puede serlo, pero no todo trait necesariamente se usa como mixin. - Al mezclarlo en una clase con `extends ... with ...`, actúa como mixin. - Pero también muchos traits se usan como abstracciones de tipo/interfaz.