Substituindo o método java equals () – não está funcionando?

Eu me deparei com um problema interessante (e muito frustrante) com o método equals() hoje, que causou o crash do que eu pensava ser uma class bem testada e causou um bug que levou muito tempo para rastrear.

Apenas para completar, eu não estava usando um IDE ou um depurador – apenas um bom editor de texto antigo e um System.out. O tempo era muito limitado e era um projeto escolar.

De qualquer forma –

Eu estava desenvolvendo um carrinho de compras básico que poderia conter um object ArrayList of Book . Para implementar os addBook() , removeBook() e hasBook() do carrinho, queria verificar se o Book já existia no Cart . Então eu vou –

 public boolean equals(Book b) { ... // More code here - null checks if (b.getID() == this.getID()) return true; else return false; } 

Tudo funciona bem nos testes. Eu crio 6 objects e preencho-os com dados. Faça muitas operações de adds, remove, has () no Cart e tudo funciona bem. Eu li que você pode ter equals(TYPE var) ou equals(Object o) { (CAST) var } mas assumiu que, uma vez que estava funcionando, não importava muito.

Então me deparei com um problema – eu precisava criar um object Book apenas com o ID dentro da class Book. Nenhum outro dado seria inserido nele. Basicamente o seguinte:

 public boolean hasBook(int i) { Book b = new Book(i); return hasBook(b); } public boolean hasBook(Book b) { // .. more code here return this.books.contains(b); } 

De repente, o método equals(Book b) não funciona mais. Isso levou um tempo muito longo para rastrear sem um bom depurador e assumindo que a class Cart foi testada e correta corretamente. Após swaapping o método equals() para o seguinte:

 public boolean equals(Object o) { Book b = (Book) o; ... // The rest goes here } 

Tudo começou a funcionar novamente. Existe uma razão pela qual o método decidiu não usar o parâmetro Book mesmo sendo claramente um object Book ? A única diferença parece ser que ela foi instanciada dentro da mesma class e preenchida apenas com um membro de dados. Estou muito muito confuso. Por favor, derramar alguma luz?

    Em Java, o método equals() herdado de Object é:

     public boolean equals(Object other); 

    Em outras palavras, o parâmetro deve ser do tipo Object .

    O ArrayList usa o método equals correto, onde você estava sempre chamando o que não substituía corretamente os Object de Object .

    Não replace o método corretamente pode causar problemas.

    Eu replace igual a seguinte toda vez:

     @Override public boolean equals(Object other){ if (other == null) return false; if (other == this) return true; if (!(other instanceof MyClass))return false; MyClass otherMyClass = (MyClass)other; ...test other properties here... } 

    O uso da anotação @Override pode ajudar uma tonelada com erros bobos.

    Use-o sempre que achar que está substituindo o método de uma superclass ou interface. Dessa forma, se você fizer isso errado, você receberá um erro de compilation.

    Se você usar o eclipse, vá até o menu superior

    Fonte -> Gerar equals () e hashCode ()

    Um pouco fora do tópico para a sua pergunta, mas provavelmente vale a pena mencionar de qualquer maneira:

    O Commons Lang tem alguns methods excelentes que você pode usar para replace equals e hashcode. Verifique EqualsBuilder.reflectionEquals (…) e HashCodeBuilder.reflectionHashCode (…) . Salvou-me muita dor de cabeça no passado – embora, claro, se você só quer fazer “iguais” na identificação, pode não se encheckboxr nas suas circunstâncias.

    Eu também concordo que você deve usar a anotação @Override sempre que você estiver substituindo equals (ou qualquer outro método).

    Outra solução rápida que salva código clichê é a anotação Lombok EqualsAndHashCode . É fácil, elegante e personalizável. E não depende do IDE . Por exemplo;

     import lombok.EqualsAndHashCode; @EqualsAndHashCode(of={"errorNumber","messageCode"}) // Will only use this fields to generate equals. public class ErrorMessage{ private long errorNumber; private int numberOfParameters; private Level loggingLevel; private String messageCode; 

    Veja as opções disponíveis para personalizar quais campos usar nos iguais. Lombok é avalaible em maven . Basta adicioná-lo com o escopo fornecido :

      org.projectlombok lombok 1.14.8 provided  

    no Android Studio é alt + insert —> equals e hashCode

    Exemplo:

      @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Proveedor proveedor = (Proveedor) o; return getId() == proveedor.getId(); } @Override public int hashCode() { return getId(); } 

    Considerar:

     Object obj = new Book(); obj.equals("hi"); // Oh noes! What happens now? Can't call it with a String that isn't a Book... 

    a instrução instanceOf é frequentemente usada na implementação de iguais.

    Esta é uma armadilha popular!

    O problema é que usar instanceOf viola a regra de simetria:

    (object1.equals(object2) == true) se e somente se (object2.equals(object1))

    se o primeiro equals for true e object2 for uma instância de uma subclass da class na qual obj1 pertence, então o segundo equals retornará false!

    se a class considerada onde ob1 pertence for declarada como final, então este problema não pode surgir, mas em geral, você deve testar da seguinte maneira:

    this.getClass() != otherObject.getClass(); se não, retorne false, caso contrário, teste os campos para comparar por igualdade!

    recordId é propriedade do object

     @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Nai_record other = (Nai_record) obj; if (recordId == null) { if (other.recordId != null) return false; } else if (!recordId.equals(other.recordId)) return false; return true; }