Il metodo equals per il confronto degli oggetti – Lezione 10 di Java Avanzato

Il metodo equals: come confrontare due oggetti?

Dover confrontare due oggetti è un’operazione molto rincorrente in qualsiasi programma. L’operatore binario == viene utilizzato per stabilire se il contenuto di due variabili è identico. Per i tipi non primitivi viene confrontato il contenuto dei puntatori degli oggetti, ovvero l’indirizzo di memoria degli oggetti a cui i puntatori fanno riferimento.
Spesso però, è utile considerare due oggetti distinti non solo in base ad un confronto sugli indirizzi di memoria, ma in base a specifici criteri dettati dal contesto applicativo in cui ci si trova. Potremmo avere oggetti che nonostante puntino ad indirizzi di memoria differenti possono essere considerati a tutti gli effetti uguali.
La procedura standard per confrontare oggetti in Java è il metodo equals, definito in Object:

public boolean equals(Object x)

Questo metodo prende in ingresso un oggetto x e restituisce true se l’oggetto (this) è uguale ad x, false altrimenti. Siccome il metodo equals si trova in Object, il confronto viene fatto con l’operatore ==, dato che non è possibile conoscere in Object come confrontare qualsiasi oggetto Java. Per effettuare un confronto migliore non basato sugli indirizzi di memoria, è buona norma effettuare l’overriding del metodo equals in ogni classe che sviluppiamo.

Come effettuare l’overriding di equals?

L’overriding del metodo equals deve essere definito in base al criterio di uguaglianza dettato dal contesto. Ad esempio, avendo una classe Persona con campi nome, cognome e indirizzo, il contesto applicativo ci dirà se considerare due persone uguali se hanno lo stesso cognome, oppure se hanno lo stesso nome e cognome, o se hanno stesso nome e indirizzo, ecc.

Per sovrapporre il metodo equals correttamente dovremo:

  1. Verificare se l’oggetto passato al metodo è del tipo desiderato, effettuando un controllo con l’operatore instanceof, in caso negativo restituiremo false;
  2. Altrimenti, essendo sicuri di avere un oggetto del tipo desiderato, potremo effettuare in sicurezza un downcast al tipo desiderato. In questo modo possiamo accedere ai campi e metodi prima nascosti a causa del riferimento di tipo Object utilizzato;
  3. A questo punto è possibile effettuare il vero e proprio confronto in base ai criteri dettati dal contesto e determinare se i due oggetti confrontati sono identici.

Fissiamo questi concetti con un esempio. Data una classe Employee:

  public class Employee {
    private String name;
    private int salary;
    private Employee boss;
    //...
  }

Supponiamo che venga utilizzata una struttura ad albero, in cui il presidente non ha nessun capoufficio (boss == null). Una possibile implementazione del metodo equals per la classe Employee è la seguente:

  public boolean equals(Object o) {

    if( !(o instanceof Employee)) return false;

    Employee e = (Employee) o;

    if( name.equals(e.name) &&
    ( boss == e.boss ||
    ( boss != null &&
      boss.equals( e.boss )))) return true;
    return false;
  }

La condizione dell’if consente di confrontare sia impiegati che il presidente a capo dell’azienda.

Quando si effettua l’overriding di equals, è sconsigliato cambiare il tipo del parametro di equals Object , altrimenti non avremo un overriding ma un overloading. Questo cambiamento potrebbe provocare disastri, vediamo con un esempio in cui cambiando il tipo del parametro di equals cosa potrebbe accadere:

  public boolean equals(Employee e){}

Inoltre, supponendo di avere:

  Emplyee e1 = new Employee(...);
  Emplyee e2 = new Employee(...);
  Object o1 = e1;
  Object o2= e1;

Binding dinamico di equals. Analizziamo cosa succede ad ogni riga con l’utilizzo del Binding dinamico:

  • o1.equals(o2); Le firme candidate sono cercate solo in Object perché il tipo dichiarato di o1 è Object. Di conseguenza non verrà considerata la specializzazione del metodo equals effettuata;
  • o1.equals(e2); Nella fase 2 del Binding dinamico non c’è overriding , quindi verrà preso l’unico metodo equals esattamente uguale che sarà quello contenuto in Object. Anche in questo caso non verrà considerata la specializzazione del metodo equals che abbiamo effettuato;
  • e1.equals(o1); Viene valutato ma Object non è assegnabile a Employee. Di conseguenza non verrà considerata la specializzazione del metodo equals effettuata;
  • e1.equals(e2); La firma combacia con quella specializzata e sarà l’unico caso in cui verrà chiamato il “giusto” equals.

Proprietà del metodo equals

Il linguaggio Java richiede che qualunque ridefinizione del metodo equals rispetti determinate proprietà che forniscono una vera e propria relazione di equivalenza:

  1. Riflessività, per ogni oggetto x, avremo che x.equals(x) è vero;
  2. Simmetria, dati due oggetti x ed y, avremo che se x.equals(y) è vero , allora y.equals(x) è vero;
  3. Transitività, dati tre oggetti x, y e z, se x.equals(y) è vero e y.equals(z) è vero, allora x.equals(z) è vero.

Metodo equals ed eredità. Quando il metodo equals viene sovrascritto bisogna prestare attenzione al funzionamento tra il metodo equals definito e il suo funzionamento nelle classi sottostanti. In definitiva bisogna decidere come si deve comportare il metodo equals con le sue sottoclassi. In particolare, bisogna porsi due domande:

  1. Il criterio di confronto tra oggetti di una sottoclasse è diverso da quello che vale tra oggetti della superclasse?

    • No, allora bisogna definire equals della superclasse final in modo che nessuna sottoclasse possa sovrascriverlo;
    • Si, allora il metodo equals deve essere opportunamente ridefinito in ogni sottoclasse in cui il criterio di confronto è differente.
  2. Un oggetto di una sottoclasse può essere considerato uguale ad un oggetto di una superclasse?
    • No, allora bisogna verificare di ammettere il confronto solo tra agli oggetti del tipo giusto;
    • Si, allora se gli oggetti di una sottoclasse possono essere considerati uguali a quelli di una superclasse dovremo ammettere al confronto tutti gli oggetti di quel tipo e tutte le sue sottoclassi.
Indice Lezione PrecedenteLezione Successiva

Pubblicato in Guide, Java, Programmazione Taggato con: , , , ,

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

*