2012-09-02 16 views
10

Quando ho letto un libro Java, l'autore ha affermato che, quando si progetta una classe, è in genere non sicuro utilizzare equals() con ereditarietà. Per esempio:Perché non dovrei usare uguali con ereditarietà?

public final class Date { 
    public boolean equals(Object o) { 
     // some code here 
    } 
} 

Nella classe di cui sopra, dovremmo mettere final, in modo che altri classe non può ereditare da questo. E la mia domanda è: perché non è sicuro quando consente ad un'altra classe di ereditare da questo?

+0

'Comparable' specifica' compareTo', non 'equals', a proposito. – oldrinb

+0

Oh. grazie :) Ho modificato: D – hqt

+0

Qual è il nome del libro? –

risposta

19

Perché è difficile (impossibile?) Farlo in modo corretto, in particolare lo symmetric property.

Supponiamo di avere la classe Vehicle e la classe Car extends Vehicle. Vehicle.equals() produce true se l'argomento è anche un Vehicle e ha lo stesso peso. Se si desidera implementare Car.equals() dovrebbe cedere true solo se l'argomento è anche una macchina, e ad eccezione di peso, si deve anche confrontare fare, motore, ecc

Ora immaginate il seguente codice:

Vehicle tank = new Vehicle(); 
Vehicle bus = new Car(); 
tank.equals(bus); //can be true 
bus.equals(tank); //false 

Il primo confronto potrebbe dare true se per coincidenza serbatoio e bus hanno lo stesso peso. Ma dal momento che il serbatoio non è un'auto, il confronto con un'auto produrrà sempre false.

Si hanno pochi work-around:

  • rigorosa: due oggetti sono uguali se e solo se hanno esattamente lo stesso tipo (e tutte le proprietà sono uguali). Questo è male, ad es. quando sottoclassi a malapena per aggiungere qualche comportamento o decorare la classe originale. Alcuni framework sottoclassi anche le classi senza che tu te ne accorga (Hibernate, Spring AOP con proxy CGLIB ...)

  • allentato: due oggetti sono uguali se i loro tipi sono "compatibili" e hanno lo stesso contenuto (semanticamente). Per esempio. due set sono uguali se contengono gli stessi elementi, non importa che uno sia HashSet e l'altro sia TreeSet (grazie @veer per averlo indicato).

    Questo può essere fuorviante. Prendi due LinkedHashSet s (dove l'ordine di inserimento è valido come parte del contratto). Tuttavia, poiché equals() richiede solo crudo Set contratto di conto, i rendimenti di confronto true anche per gli oggetti ovviamente diverse:

    Set<Integer> s1 = new LinkedHashSet<Integer>(Arrays.asList(1, 2, 3)); 
    Set<Integer> s2 = new LinkedHashSet<Integer>(Arrays.asList(3, 2, 1)); 
    System.out.println(s1.equals(s2)); 
    

Vedi anche

+1

Non * impossibile *; vedere per es. ['Set.equals'] (http://docs.oracle.com/javase/7/docs/api/java/util/Set.html#equals (java.lang.Object)) – oldrinb

+0

@veer: Ho aggiornato il mio rispondi, grazie. –

+0

@TomaszNurkiewicz come mi viene fornito dal link, se usiamo 'if (this.getClass()! = Tank.getClass()) {return false;}' or' if (this.getClass()! = Tank.getClass()) {return false;} ', il problema verrà risolto. Puoi dirmi di più su questo caso. – hqt

8

Martin Odersky (la ragazzo dietro generici in Java e il codebase originale per l'attuale javac) ha un bel capitolo nel suo libro Programmazione in Scala che risolve questo problema. Suggerisce che l'aggiunta di un metodo canEqual può risolvere il problema di uguaglianza/ereditarietà.È possibile leggere la discussione nella prima edizione del suo libro, che è disponibile online:

Chapter 28 of Programming in Scala, First Edition: Object Equality

Il libro è, naturalmente, riferendosi alla Scala, ma le stesse idee applicate al classico Java. Il codice sorgente di esempio non dovrebbe essere troppo difficile da comprendere per chi proviene da uno sfondo Java.

Edit:

Sembra Odersky ha pubblicato un articolo sullo stesso concetto in Java nel 2009, ed è disponibile sullo stesso sito:

How to Write an Equality Method in Java

davvero non pensare che cercare di riassumere l'articolo in questa risposta lo renderebbe giustizia. Tratta il tema dell'uguaglianza degli oggetti in profondità, dagli errori comuni compiuti nelle implementazioni di uguaglianza a una discussione completa di Java equals come relazione di equivalenza. Dovresti davvero leggerlo.