Lorsque vous comparez des objets en Java, vous effectuez une vérification sémantique , comparant le type et l’état d’identification des objets à:
- lui-même (même instance)
- lui-même (clone ou copie reconstruite)
- d'autres objets de types différents
- autres objets du même type
null
Règles:
- Symétrie :
a.equals(b) == b.equals(a)
equals()
cède toujours true
ou false
, mais jamais un NullpointerException
, ClassCastException
ou tout autre jetable
Comparaison:
- Vérification de type : les deux instances doivent être du même type, ce qui signifie que vous devez comparer les classes réelles pour l'égalité. Cela n'est souvent pas correctement implémenté, lorsque les développeurs utilisent
instanceof
pour la comparaison de types (qui ne fonctionne que tant qu'il n'y a pas de sous-classes et enfreint la règle de symétrie quand A extends B -> a instanceof b != b instanceof a)
.
- Vérification sémantique de l'état d'identification : assurez-vous de comprendre par quel état les instances sont identifiées. Les personnes peuvent être identifiées par leur numéro de sécurité sociale, mais pas par la couleur de leurs cheveux (peuvent être teints), leur nom (peut être changé) ou l'âge (change tout le temps). Vous ne devez comparer l'état complet qu'avec les objets de valeur (tous les champs non transitoires), sinon ne vérifiez que ce qui identifie l'instance.
Pour votre 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);
}
Classe d'utilité générique réutilisable:
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)));
}
}
Pour votre Person
classe, en utilisant cette classe utilitaire:
public boolean equals(Object obj) {
return Equals.as(this, obj, t -> t.name, t -> t.age);
}