====== Confronti tra elementi ====== PREV [[java:generics:subtiping_wildcards]] NEXT [[java:generics:dichiarazioni_di_classi_generiche]] Parleremo di Comparable * Confrontare elementi * Trovare massimo e minimo in una collezione * Metodi bridge ===== Comparable ===== public interface Comparable { public int compareTo(T elem); } Il metodo **compareTo** restituisce un valore che è negativo, zero o positivo a seconda che il parametro fornito sia rispettivamente minore, uguale o maggiore del parametro implicito (this). Quando una classe implementa Comparable l'ordine specifcato dalla sua interfaccia è chiamato ordine naturale per la classe. ==== Mele con mele e pere con pere ==== Integer i0 = 0; Integer i1 = 1; assert i0.compareTo(i1) < 0; String s0 = "zero"; String s1 = "uno"; assert s0.compareTo(s1) > 0; (Di norma) un oggetto di una classe può essere confrontato solo con oggetti della stessa classe (pere con pere, mele con mele) :!: Posso confrontare mele con pere? Number n1 = 1; Number npi = 3.14; assert n1.compareTo(npi) <0; // Errore di compilazione * Notare il boxing * Il codice non compila! * Number non implementa Comparable ==== Consistente con l'uguaglianza ==== x.equals(y) ↔ x.compareTo(y) == 0 Attenzione all'inserimento in SortedList ecc … compareTo VS equals * x.equals(null) → true, false * x.compareTo(null) → NullPointerException BigDecimal non è consistente con l'uguaglianza ==== Contratto per comparable ==== * **Antisimmetrica** * sgn(x.compareTo(y)) = - sgn(y.compareTo(x)) * **Transitiva** * x.compareTo(y) < 0 & y.compareTo(z) < 0 → x.compareTo(z) <0 * **Congruenza** * x.compareTo(y) == 0 → sgn(x.compareTo(z)) == sgn(y.compareTo(z)) * **Rifessiva** * x.compareTo(x) == 0 //sgn(x) è il segno di x: -1 negativo, 0 zero, 1 positivo// ==== Overflow / underflow ==== :!: Attenzione public int compareTo(Integer o) { return this.value - o.value; } NO! Può generare overfow: confrontando un numero negativo grande in modulo con un grande numero positivo → Si può superare Integer.MAX_VALUE **Overflow**: * Numero troppo grande in valore assoluto Maggiore di Integer.MAX_VALUE o minore di Integer.MIN_VALUE **Underfow**: * numero, diverso da zero, troppo piccolo in valore assoluto per essere rappresentato dalla macchina ===== Trovare il massimo di una collezione ===== public static > T max (Collection coll){ T cand = coll.iterator().next(); for (T elem: coll){ if (cand.compareTo(elem) < 0) cand = elem; } return cand; } ==== Limiti ==== > si dice che T è limitato da Comparable T Si è quindi posto un limite sul tipo di T. Il limite può essere ricorsivo , U extends D> ==== Esempi ==== List stringhe = Arrays.asList("ciao","mondo"); // ok assert Collections.max(stringhe).equals("mondo" ); // ok List interi = Arrays.asList(1,2,3); assert Collections.max(interi) == 2; // ok List numeri = Arrays.asList(1 ,2 ,3 ,4 ,5 ,3.14); // ok assert Collections.max(numeri) == 5; // NO! Il codice con compila ==== E' possibile utilizzare firme il più generiche possibile ==== public static > T max (Collection coll) public static > T max ( Collection elements) public static > T max( Collection coll ) { ==== Limiti multipli ==== public static void copy(S src, T dest, int dim)throws IOException{ CharBuffer buff = CharBuffer.allocate(dim); int i = src.read(buff); while (i >= 0 ){ buff.flip(); // prepara per la scrittura dest.append(buff); buff.clear(); i = src.read(buff); } src.close(); dest.close(); } ===== Metodi bridge ===== Generics sono implementati mediante [[type erasure]] * Il bytecode è piuttosto simile (e compatibile) con la versione senza Generics Classi che implementano interfacce generiche (es: Comparable ) * Vengono aggiunti dal compilatore alcuni metodi detti metodi bridge public class Foo1 implements Comparable { private final String value ; public Foo1(String value) { super(); this.value = value; } public int compareTo(Object o) { return compareTo((Foo1)o); } public int compareTo(Foo1 o){ return value.compareTo(o.value); } } Esempio di Comparable senza Generics. Il metodo classico chiama il metodo ridefnito dopo un cast (double dispatch). Attenzione! Java supporta il binding dinamico solamente sull'argomento implicito (this) e non sui parametri formali (quelli tra parentesi) ==== Con i generics ==== public class Foo2 implements Comparable { private final String value; public Foo2(String value) { this.value = value; } public int compareTo(Foo2 o) { return value.compareTo(o.value); } } C'è solo un metodo nel codice sorgente Vediamo cosa succede nel bytecode generato dal compilatore ... ==== Bytecode generato con metodo bridge ==== for (Method m : Foo2.class.getMethods()){ if (m.getName() .equals("compareTo")) { System.out.println( m.toGenericString()); } } ---------------------- public int Foo2.compareTo(Foo2) public bridge int Foo2.compareTo(java.lang.Object) ==== Override covariante (finalmente!) ==== In java <= 1.4 un metodo può sovrascrivere un altro ↔ le due frme coincidono esattamente. In java >= 5 un metodo può sovrascrivere un altro se gli argomenti sono identici ed il tipo di ritorno del metodo riscrivente è un sottotipo del tipo di ritorno del metodo riscritto. ==== Override <= 1.4 ==== public class OldPunto { private final int x; private final int y; public OldPunto(int x, int y) { this.x = x; this.y = y; } public Object clone() { return new OldPunto(x, y); **Object ha un metodo clone() che restituisce un __Object__** ==== Override >= 5.0 ==== public class NewPunto { private final int x; private final int y; public NewPunto(int x, int y) { this.x = x; this.y = y; } public NewPunto clone() { return new NewPunto(x, y); } } NewPunto è sottotipo di Object quindi __non ci sono errori di compilazione__ Vediamo se ci sono dei bridge: public NewPunto NewPunto.clone() public bridge java.lang.Object NewPunto.clone() throws java.lang.CloneNotSupportedException ==== Ancora sui bridge ==== A seconda del compilatore la dicitura bridge: * Può non comparire! * Può essere sostituita da volatile! (bug) * Altro … * Nel compilatore java6 di Apple non compare bridge