Corri para um problema interessante (e muito frustrante) com o equals()
método hoje, que causou o travamento do que eu pensava ser uma classe bem testada e causou um bug que levou muito tempo para ser rastreado.
Apenas para completar, eu não estava usando um IDE ou depurador - apenas um bom editor de texto à moda antiga e 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 ArrayList
dos Book
objetos . A fim de implementar as addBook()
, removeBook()
e hasBook()
métodos do carrinho, eu 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. Crio 6 objetos e os preencho com dados. Faça muitas adições, remoções, operações com () Cart
e tudo funciona bem. Eu li que você pode ter equals(TYPE var)
ouequals(Object o) { (CAST) var }
presumiu que, como estava funcionando, não importava muito.
Então eu corri para um problema - eu precisava para criar um Book
objeto com única a ID
nele de dentro da classe 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 equals(Book b)
método não funciona mais. Demorou MUITO tempo para rastrear sem um bom depurador e assumindo que a Cart
classe foi testada e correta corretamente. Depois de trocar o equals()
método 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 que claramente fosse um Book
objeto? A única diferença parecia ser instanciada na mesma classe e preenchida apenas com um membro dos dados. Estou muito, muito confusa. Por favor, lançar alguma luz?
fonte
Respostas:
Em Java, o
equals()
método herdadoObject
é:Em outras palavras, o parâmetro deve ser do tipo
Object
. Isso é chamado de substituição ; seu métodopublic boolean equals(Book other)
faz o que é chamado de sobrecarga para oequals()
método.Os métodos
ArrayList
substituídos são usadosequals()
para comparar o conteúdo (por exemplo, para os métodoscontains()
eequals()
) e não para os sobrecarregados. Na maioria do seu código, chamar o que não substituiu corretamenteObject
os iguais foi bom, mas não compatívelArrayList
.Portanto, não substituir o método corretamente pode causar problemas.
Eu substituo é igual ao seguinte sempre:
O uso da
@Override
anotação pode ajudar bastante com erros tolos.Use-o sempre que achar que está substituindo o método de uma superclasse ou interface. Dessa forma, se você fizer da maneira errada, receberá um erro de compilação.
fonte
if (!(other instanceof MyClass))return false;
retornafalse
seMyClass
estende a outra classe. Mas não voltariafalse
se a outra classe se estendesseMyClass
. Não deveriaequal
ser menos contraditório?Se você usa eclipse, basta ir ao menu superior
fonte
Ligeiramente fora de tópico para sua pergunta, mas provavelmente vale a pena mencionar de qualquer maneira:
O Commons Lang possui alguns métodos excelentes que você pode usar para substituir iguais e hashcode. Confira EqualsBuilder.reflectionEquals (...) e HashCodeBuilder.reflectionHashCode (...) . Me salvou muita dor de cabeça no passado - embora, é claro, se você quiser apenas "igual" ao ID, ele pode não se encaixar nas suas circunstâncias.
Concordo também que você deve usar a
@Override
anotação sempre que substituir valores iguais (ou qualquer outro método).fonte
right click -> source -> generate hashCode() and equals()
,Outra solução rápida que salva o código padrão é a anotação Lombok EqualsAndHashCode . É fácil, elegante e personalizável. E não depende do IDE . Por exemplo;
Veja as opções disponíveis para personalizar quais campos usar nos iguais. Lombok está disponível em maven . Basta adicioná-lo com o escopo fornecido :
fonte
no Android Studio é alt + insert ---> equals e hashCode
Exemplo:
fonte
Considerar:
fonte
obj
é declarado como umObject
. O ponto da herança é que você pode atribuir umBook
aobj
. Depois disso, a menos que você sugira que umObject
não deve ser comparável a umString
viaequals()
, esse código deve ser perfeitamente legal e retornarfalse
.a
instanceOf
declaração é freqüentemente usada na implementação de iguais.Esta é uma armadilha popular!
O problema é que o uso
instanceOf
viola a regra da simetria:(object1.equals(object2) == true)
se e apenas se(object2.equals(object1))
se o primeiro igual é verdadeiro e o objeto2 é uma instância de uma subclasse da classe à qual o obj1 pertence, o segundo igual retornará falso!
se a classe considerada à qual ob1 pertence for declarada como final, esse problema não poderá surgir, mas, em geral, você deve testar da seguinte maneira:
this.getClass() != otherObject.getClass();
Caso contrário, retorne false; caso contrário, teste os campos para comparar a igualdade!fonte
equals()
método. Ele recomenda não usargetClass()
. A principal razão é que isso infringe o Princípio de Substituição de Liskov para subclasses que não afetam a igualdade.recordId é propriedade do objeto
fonte