Quando si confrontano oggetti in Java, si effettua un controllo semantico , confrontando il tipo e lo stato di identificazione di degli oggetti con:
- stesso (stessa istanza)
- stesso (clone o copia ricostruita)
- altri oggetti di diverso tipo
- altri oggetti dello stesso tipo
null
Regole:
- Simmetria :
a.equals(b) == b.equals(a)
equals()
cede sempre true
o false
, ma mai a NullpointerException
, ClassCastException
o qualsiasi altro oggetto lanciabile
Confronto:
- Controllo del tipo : entrambe le istanze devono essere dello stesso tipo, il che significa che devi confrontare le classi effettive per l'uguaglianza. Questo spesso non è implementato correttamente, quando gli sviluppatori usano
instanceof
per il confronto dei tipi (che funziona solo finché non ci sono sottoclassi e viola la regola di simmetria quando A extends B -> a instanceof b != b instanceof a)
.
- Controllo semantico dello stato di identificazione : assicurarsi di aver compreso con quale stato vengono identificate le istanze. Le persone possono essere identificate dal numero di previdenza sociale, ma non dal colore dei capelli (possono essere tinti), nome (può essere cambiato) o età (cambia continuamente). Solo con gli oggetti valore dovresti confrontare lo stato completo (tutti i campi non transitori), altrimenti controlla solo ciò che identifica l'istanza.
Per la tua Person
classe:
public boolean equals(Object obj) {
// same instance
if (obj == this) {
return true;
}
// null
if (obj == null) {
return false;
}
// type
if (!getClass().equals(obj.getClass())) {
return false;
}
// cast and compare state
Person other = (Person) obj;
return Objects.equals(name, other.name) && Objects.equals(age, other.age);
}
Riutilizzabile, classe di utilità generica:
public final class Equals {
private Equals() {
// private constructor, no instances allowed
}
/**
* Convenience equals implementation, does the object equality, null and type checking, and comparison of the identifying state
*
* @param instance object instance (where the equals() is implemented)
* @param other other instance to compare to
* @param stateAccessors stateAccessors for state to compare, optional
* @param <T> instance type
* @return true when equals, false otherwise
*/
public static <T> boolean as(T instance, Object other, Function<? super T, Object>... stateAccessors) {
if (instance == null) {
return other == null;
}
if (instance == other) {
return true;
}
if (other == null) {
return false;
}
if (!instance.getClass().equals(other.getClass())) {
return false;
}
if (stateAccessors == null) {
return true;
}
return Stream.of(stateAccessors).allMatch(s -> Objects.equals(s.apply(instance), s.apply((T) other)));
}
}
Per la tua Person
classe, utilizzando questa classe di utilità:
public boolean equals(Object obj) {
return Equals.as(this, obj, t -> t.name, t -> t.age);
}