Ao comparar objetos em Java, você faz uma verificação semântica , comparando o tipo e identificando o estado dos objetos para:
- em si (mesma instância)
- em si (clone ou cópia reconstruída)
- outros objetos de diferentes tipos
- outros objetos do mesmo tipo
null
Regras:
- Simetria :
a.equals(b) == b.equals(a)
equals()
sempre cede true
ou false
, mas nunca um NullpointerException
, ClassCastException
ou qualquer outro lançável
Comparação:
- Verificação de tipo : ambas as instâncias precisam ser do mesmo tipo, o que significa que você deve comparar as classes reais para igualdade. Isso geralmente não é implementado corretamente quando os desenvolvedores usam
instanceof
para comparação de tipo (o que só funciona enquanto não houver subclasses e viola a regra de simetria quando A extends B -> a instanceof b != b instanceof a)
.
- Verificação semântica do estado de identificação : certifique-se de entender por qual estado as instâncias são identificadas. As pessoas podem ser identificadas pelo número do seguro social, mas não pela cor do cabelo (pode ser tingido), nome (pode ser alterado) ou idade (muda o tempo todo). Apenas com objetos de valor você deve comparar o estado completo (todos os campos não transitórios), caso contrário, verifique apenas o que identifica a instância.
Para sua Person
aula:
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 de utilitário genérico reutilizável:
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)));
}
}
Para sua Person
classe, usando esta classe de utilitário:
public boolean equals(Object obj) {
return Equals.as(this, obj, t -> t.name, t -> t.age);
}