Pergunta

Eu corri para um interessante (e muito frustrante) problema com o método equals() hoje que causou o que eu pensava ser uma classe bem testado a falhar e causar um erro que me levou muito tempo para rastrear.

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

De qualquer forma -

Eu estava desenvolvendo um carrinho de compras básico que poderia conter um ArrayList de Book objetos . A fim de implementar os métodos addBook(), removeBook() e hasBook() do carro, eu queria verificar se o Book já existia no Cart. Então lá vou eu -

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

Tudo funciona bem em testes. Eu crio 6 objetos e preenchê-los com dados. Fazer muitas adiciona, remove, tem () operações no Cart e tudo funciona bem. Eu li que você pode quer ter equals(TYPE var) ou equals(Object o) { (CAST) var } , mas assumiu que uma vez que estava trabalhando, ele fez não importa muito.

Então eu corri para um problema - eu precisava para criar um objeto Book com única o 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, obras o método equals(Book b) por mais tempo. Isso levou um tempo muito longo para rastrear sem um bom depurador e assumindo a classe Cart foi devidamente testado e correta. Depois swaapping o método equals() com o seguinte:

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

Tudo começou a trabalhar novamente. Existe uma razão para o método decidiu não tomar o parâmetro Livro embora claramente foi um objeto Book? A única diferença parecia ser que foi instanciado a partir de dentro da mesma classe, e apenas preenchido com um membro de dados. Eu estou muito, muito confuso. Por favor, lançar alguma luz?

Foi útil?

Solução

Em Java, o método equals() que é 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 correta, onde foram sempre chamando a única que não substituem adequadamente iguais de Object.

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

Eu override é igual ao seguinte cada vez que:

@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 você pensa você está substituindo uma classe de super ou método da interface. Dessa forma, se você fizer isso errado, você receberá um erro de compilação.

Outras dicas

Se você usar eclipse basta ir ao menu superior

Source -> Gerar equals () e hashCode ()

Um pouco off-topic à sua pergunta, mas é provavelmente vale a pena mencionar qualquer maneira:

Commons Lang tem alguns excelentes métodos que você pode usar em substituir equals e hashcode. Confira EqualsBuilder.reflectionEquals (...) e HashCodeBuilder.reflectionHashCode (...) . me salvou muita dor de cabeça no passado -. embora, é claro, se você só quer fazer "iguais" no ID pode não atender às suas circunstâncias

Também concordo que você deve usar a anotação @Override sempre que você está substituindo iguais (ou qualquer outro método).

Outra solução rápida que salva código clichê é Lombok EqualsAndHashCode anotação . É 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 a opções disponível para personalizar quais campos para uso nos iguais. Lombok é avalaible em maven . Basta adicionar-lo com fornecido escopo:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.14.8</version>
    <scope>provided</scope>
</dependency>

no Android Studio é alt + insert ---> iguais 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();
}

Considere o seguinte:

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 declaração instanceOf é frequentemente utilizado na implementação de iguais.

Esta é uma armadilha popular!

O problema é que o uso de instanceOf viola a regra de simetria:

(object1.equals(object2) == true) se e só se (object2.equals(object1))

Se os primeiros iguais é verdadeiro, e object2 é uma instância de uma subclasse de a classe onde obj1 pertence, então a segunda iguais retornará false!

Se a classe respeitava onde OB1 pertence é declarado como final, então este problema não pode ocorrer, mas em geral, você deve testar o seguinte:

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

recordId é propriedade do objeto

@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;
    }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top