2015-06-23 24 views
12

J. Bloch nel suo Java efficace fornisce diverse regole per l'implementazione per il metodo di uguaglianza. Eccoli:Metodo di comprensione degli uguali

• Riflessivo: Per ogni valore di riferimento non nullo x, x.equals (x) devono restituire true.

• Simmetrico: per qualsiasi valore di riferimento non nullo x e y, x.equals (y) deve restituire true se e solo se y.equals (x) restituisce true.

• transitiva: Per non nullo valori di riferimento x, y, z, se x.equals (y) restituisce true e y.equals (z) restituisce true, quindi x.equals (z) deve tornare vero.

• Coerente: Per qualsiasi riferimento non nullo valori x ed y, più invocazioni x.equals (y) costantemente ritorno vere o costantemente ritorno falso, fornito informazioni utilizzate in uguale confronto sugli oggetti viene modificato .

• Per qualsiasi valore di riferimento xnon valido, x.equals (null) deve restituire false.

Ma più tardi nel libro ha citato cosiddetto Liskov principio di sostituzione:

Il principio di sostituzione Liskov dice che qualsiasi proprietà importante di un tipo dovrebbe valere anche per i suoi sottotipi, in modo che qualsiasi metodo scritta per il tipo dovrebbe funzionare altrettanto bene sui suoi sottotipi

non vedo come si collega ai equals contratti. Dovremmo effettivamente aderirvi mentre scriviamo l'implementazione degli uguali?

La domanda riguarda l'implementazione del metodo per le sottoclassi. Ecco l'esempio dal libro:

private static final Set<Point> unitCircle; 

static { 
    unitCircle = new HashSet<Point>(); 
    unitCircle.add(new Point(1, 0)); 
    unitCircle.add(new Point(0, 1)); 
    unitCircle.add(new Point(-1, 0)); 
    unitCircle.add(new Point(0, -1)); 
} 

public static boolean onUnitCircle(Point p) { 
    return unitCircle.contains(p); 
} 

public class CounterPoint extends Point { 
    private static final AtomicInteger counter = new AtomicInteger(); 

    public CounterPoint(int x, int y) { 
     super(x, y); 
     counter.incrementAndGet(); 
    } 

    public int numberCreated() { return counter.get(); } 
} 

e la seguente implementazione:

// Broken - violates Liskov substitution principle (page 40) 
@Override public boolean equals(Object o) { 
    if (o == null || o.getClass() != getClass()) 
     return false; 
    Point p = (Point) o; 
    return p.x == x && p.y == y; 
} 

Ok, viola e che cosa poi? Non capisco.

+5

anche le superclassi non dovrebbero conoscere i suoi figli, ma l'oggetto sa che la classe stringa^^ java non è affatto ben progettata. – Zelldon

+2

@Zelldon Si prega di elaborare, cosa intendi per "oggetto conosce stringa"? (Non sto seguendo la richiesta) – amit

+2

L'oggetto ha il metodo 'toString()'. –

risposta

6

ci sono in genere 2 modi per controllare il tipo nel metodo equals:

Opzione 1: instanceof

if (! (obj instanceof ThisClass)){ 
    return false; 
} 

Questa opzione aspetti il Liskov principio di sostituzione. Ma non puoi aggiungere proprietà aggiuntive in sottoclassi che sono rilevanti per il metodo equals senza rompere le caratteristiche di una relazione di equivalenza (riflessiva, simmetrica, transitiva).

Opzione 2: getClass()

if (obj == null || ! this.getClass().equals(obj.getClass())) { 
    return false; 
} 

Questa opzione viola il Liskov principio di sostituzione. Ma tu puoi aggiungere proprietà addizionali in sottoclassi che sono rilevanti per il metodo equals senza rompere le caratteristiche di una relazione di equivalenza (riflessiva, simmetrica, transitiva).

Joshua Bloch mette in guardia su questo nel suo libro "Efficace Java".

Angelika Langer però parla di un modo per i confronti "mixed-Tpye", se è possibile definire i valori predefiniti per le proprietà aggiuntive:

http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals-2.html

Il rovescio della medaglia è che i metodi equals diventa piuttosto complicato.

// Broken - violates Liskov substitution principle (page 40) 
@Override public boolean equals(Object o) { 
    if (o == null || o.getClass() != getClass()) 
     return false; 
    Point p = (Point) o; 
    return p.x == x && p.y == y; 
} 

Ok, viola e che cosa poi? Non capisco.

Quindi, se si dispone di una classe secondaria, come myPoint (che potrebbe aggiungere ulteriori metodi ma non proprietà aggiuntive/campi), poi

Point p1 = new Point(x, y); 
Point p2 = new MyPoint(x, y); 

p1.equals(p2) == false 

Set<Point> points = new HashSet<>(); 
points.add(p1); 

points.contains(p2) == false; 

anche se entrambi gli oggetti in realtà rappresentano lo stesso punto.

Se si utilizza l'opzione 1 (instanceof), il metodo equals restituisce true.

+0

Il fatto è che J.B. suggerisca di non usare mai l'ereditarietà. Propose di usare invece la composizione per questi casi. Questo è chiaro concetto, sì. Mi è stato chiesto dal fatto che ha detto che non possiamo implementare gli uguali in modo corretto per le sottoclassi di sempre. –

+0

Le sottoclassi delle classi astratte ovviamente non sono considerate. –

+0

Non vedo, nell'opzione 1, perché non è possibile aggiungere nuovi attributi nelle sottoclassi. Voglio dire, per la classe super uguale non controllerai gli attributi delle sottoclassi solo i propri attributi, le sottoclassi dovrebbero controllare i propri attributi. Se si confronta un oggetto con un'istanza di una sottoclasse, solo gli attributi di classe superiore sono pertinenti. – prmottajr

1

Penso che stia cercando di dire che la caratteristica di un punto è le sue coordinate. Quindi, ci si aspetta che questo è vero:

new Point(0, 0).equals(new CounterPoint(0, 0)); 

perché i due punti hanno le stesse coordinate, anche se non hanno lo stesso tipo. Ma il metodo proposto equals restituirà false perché i due oggetti hanno classi diverse.

Se si pensa di collezioni per esempio, questo è vero:

new LinkedList().equals(new ArrayList()); 

Le due liste non hanno lo stesso tipo ma hanno lo stesso contenuto (in questo caso sono entrambi vuoto) e sono quindi considerato uguale.

+0

Quindi, l'implementazione non viola il contratto equalses ma sono d'accordo, sembra controintuitivo. È questa la ragione per cui ci suggerisce di non scrivere quals in questo modo? –

Problemi correlati