Question

J'ai rencontré un problème intéressant (et très frustrant) avec la méthode equals () aujourd'hui, ce qui a provoqué un crash de ce que je pensais être une classe bien testée et un bogue qui m'a pris très mal. long temps à traquer.

Juste pour être complet, je n’utilisais pas d’EDI ou de débogueur, c’était un bon vieux éditeur de texte et System.out. Le temps était très limité et c’était un projet d’école.

Quoi qu'il en soit -

Je développais un panier d'achat de base pouvant contenir une ArrayList d'objets Book . Pour implémenter les méthodes addBook () , removeBook () et hasBook () du panier, je voulais vérifier si le < code> Le livre existait déjà dans le Panier . Alors je vais -

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

Tout fonctionne correctement pour les tests. Je crée 6 objets et les remplis de données. Faites beaucoup d’ajouts, de suppressions, d’opérations () sur le Cart et tout fonctionne correctement. J'ai lu que vous pouvez avoir égal à (TYPE var) ou égal à (Object o) {(CAST) var} mais en supposant que travaillait, cela importait peu.

Ensuite, j'ai rencontré un problème: je devais créer un objet Book avec uniquement l'identifiant ID dans la classe Book. Aucune autre donnée n'y serait entrée. En gros, ce qui suit:

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);
}

Tout à coup, la méthode égale (Livre b) ne fonctionne plus. Cela a pris TRES longtemps pour rechercher sans un bon débogueur et en supposant que la classe Cart était correctement testée et correcte. Après avoir modifié la méthode equals () , procédez comme suit:

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

Tout a recommencé à fonctionner. Existe-t-il une raison pour laquelle la méthode a décidé de ne pas utiliser le paramètre Book alors même que était clairement un objet Book ? La seule différence semblait être qu'elle était instanciée à partir de la même classe et remplie avec un seul membre de données. Je suis très très confus. S'il vous plaît, donnez un peu de lumière?

Était-ce utile?

La solution

En Java, la méthode equals () héritée de Object est:

public boolean equals(Object other);

En d'autres termes, le paramètre doit être de type Objet .

Le ArrayList utilise la méthode equals correcte, dans laquelle vous appelez toujours celle qui ne substitue pas correctement l'équivalent de Object .

Ne pas écraser correctement la méthode peut causer des problèmes.

Je remplace est égal à ce qui suit à chaque fois:

@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...
}

L'utilisation de l'annotation @Override peut aider énormément avec des erreurs stupides.

Utilisez-le chaque fois que vous pensez que vous substituez la méthode d'une super classe ou d'une interface. De cette façon, si vous le faites mal, vous obtiendrez une erreur de compilation.

Autres conseils

Si vous utilisez Eclipse, allez simplement dans le menu principal

  

Source - > Générer des équivalents () et   hashCode ()

Un peu hors sujet par rapport à votre question, mais il vaut probablement la peine de le mentionner quand même:

Commons Lang dispose de méthodes excellentes que vous pouvez utiliser pour remplacer les valeurs égales et hashcode. Consultez EqualsBuilder.reflectionEquals (...) et HashCodeBuilder.reflectionHashCode (...) . M'a sauvé beaucoup de maux de tête dans le passé - bien sûr, si vous voulez juste faire "égal à" " identifiant, il peut ne pas correspondre à votre situation.

Je conviens également que vous devriez utiliser l'annotation @Override chaque fois que vous substituez égal (ou toute autre méthode).

Une autre solution rapide qui enregistre le code standard est la l'annotation de Lombok EqualsAndHashCode . C'est facile, élégant et personnalisable. Et ne dépend pas de l'IDE . Par exemple,

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;

Voir les options disponibles pour personnaliser les champs à utiliser dans les valeurs égales. Lombok est disponible dans maven . Ajoutez-le simplement avec la portée fournie :

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

dans Android Studio est alt + insert --- > est égal à et hashCode

Exemple:

    @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();
}

Considérez:

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

L’instruction instanceOf est souvent utilisée dans l’implémentation d’égal à égal.

C’est un piège populaire!

Le problème est que l'utilisation de instanceOf enfreint la règle de symétrie:

(object1.equals (object2) == true) si et seulement si (object2.equals (object1))

si le premier égal est vrai et que object2 est une instance d'une sous-classe de la classe à laquelle obj1 appartient, le second égal retournera false!

si la classe considérée à laquelle ob1 appartient est déclarée comme finale, alors ceci problème ne peut pas se produire, mais en général, vous devriez tester comme suit:

this.getClass ()! = otherObject.getClass (); sinon, renvoie false, sinon test les champs à comparer pour l'égalité!

recordId est la propriété de l'objet

@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;
    }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top