Effective Java – Item 10 – Sobrescrever o método Equals (parte 1)

Por vezes é melhor herdar o método equals da classe Object do que sobrescrevê-lo.

Para que funcione de forma correta, o equals deve obedecer a um contrato geral, que define que ele seja:

• Reflexivo: x != null => x.equals(x) == true;
• Simétrico: x != null & y != null => x.equals(y) == true <=> y.equals(x) == true ;
• Transitivo: x != null & y != null & z != null => x.equals(y) == true & y.equals(z) == true & x.equals(z) == true;
• Consistente: x != null & y != null => x.equals(y) == true (|| false), quantas vezes for chamada.
• e quando nulo, x != null => x.equals(null) == false.

O primeiro item diz que um objeto deve ser igual a ele mesmo. Num Set, por exemplo, se essa propriedade não estiver presente, um objeto pode ser inserido duas vezes ou não removido corretamente.

Parece lógico mas o desenvolvedor pode cair em armadilhas. Vejamos como não obedecer a segunda propriedade, que diz que dois objetos devem acordar sobre a forma com eles são iguais.

// Broken - violates symmetry!
public final class CaseInsensitiveString {
private final String s;
public CaseInsensitiveString(String s) {
this.s = Objects.requireNonNull(s);
}
// Broken - violates symmetry!
@Override public boolean equals(Object o) {
if (o instanceof CaseInsensitiveString)
return s.equalsIgnoreCase(
((CaseInsensitiveString) o).s);
if (o instanceof String) // One-way interoperability!
return s.equalsIgnoreCase((String) o);
return false;
}
… // Remainder omitted
}

Ao comparar essas duas strings, a segunda propriedade será violada.

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
String s = "polish";

Ao comparar cis.equals(s) será retornado true já que ele foi construído para desconsiderar o “case”. O problema ocorre quando s.equals(cis), que retorna false, já que não ignora o “case”. Um “claro caso de violação da simetria”.

Para corrigir, o método equals deve ficar da seguinte forma.

@Override public boolean equals(Object o) {
return o instanceof CaseInsensitiveString &&
((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
}

Esqueça a tentativa de lidar com String, o que é chamado no livro de “One-way interoperability!“.

Nesse post falei da primeira e segunda propriedade, sem as quais podem ocorrer inconsistências e causar exceções.

Então até o próximo, no qual continuarei com as outras propriedades.

PS.: a especificação do método equals é encontrada em https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals(java.lang.Object).

PS.2: nada é trivial.

Leave a Reply

Your email address will not be published. Required fields are marked *