Outrepasser la méthode java equals () - ne fonctionne pas?
j'ai rencontré un problème intéressant (et très frustrant) avec la méthode equals()
aujourd'hui qui a causé ce que je pensais être une classe bien testée de planter et de causer un bug qui m'a pris un très long temps à traquer.
juste pour être complet, je n'utilisais pas un IDE ou un débogueur - juste un bon éditeur de texte à l'ancienne et un système.dehors. Le temps était très limité et c'était un projet scolaire.
anywhere -
j'ai été développer un panier d'achat de base qui pourrait contenir un ArrayList
de Book
objets . Afin de mettre en œuvre les méthodes addBook()
, removeBook()
, et hasBook()
du chariot, je voulais vérifier si le Book
existait déjà dans le Cart
. Donc, je vais -
public boolean equals(Book b) {
... // More code here - null checks
if (b.getID() == this.getID()) return true;
else return false;
}
Tout fonctionne très bien dans les tests. Je crée 6 objets et je les remplis de données. Faire beaucoup de ajoute, supprime, a() les opérations sur la Cart
et tout fonctionne très bien. J'ai lu que vous pouvez soit avoir equals(TYPE var)
ou equals(Object o) { (CAST) var }
mais supposé que depuis qu'il fonctionnait, il n'a pas trop d'importance.
puis j'ai rencontré un problème - j'ai dû créer un Book
objet avec seulement le ID
dans lui de l'intérieur de la classe de Livre. Aucune autre donnée ne serait entrée. Essentiellement le suivant:
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 d'un coup, la méthode equals(Book b)
ne fonctionne plus. Il a fallu beaucoup de temps pour le retrouver sans un bon Débogueur et en supposant que la classe Cart
était correctement testée et correcte. Après avoir remplacé la méthode equals()
par la suivante:
public boolean equals(Object o) {
Book b = (Book) o;
... // The rest goes here
}
Tout a commencé à travailler à nouveau. Y a-t-il une raison pour laquelle la méthode a décidé de ne pas prendre le paramètre de livre, bien qu'il soit clairement était a Book
objet? La seule différence semble être a été instancié à l'intérieur de la même classe, et rempli seulement avec un membre de données. Je suis très très confus. S'il vous plaît, jeter un peu de lumière?
8 réponses
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 du type Object
.
le ArrayList
utilise la méthode correcte des égaux, où vous appeliez toujours celui qui n'a pas correctement outrepassé les égaux de Object
.
ne pas dépasser la méthode correctement peut causer des problèmes.
I override égale le à 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 une tonne avec des erreurs stupides.
utilisez-le chaque fois que vous pensez que vous supplantez une classe super' ou la méthode de l'interface. De cette façon, si vous le faites mal, vous obtiendrez une erreur de compilation.
si vous utilisez eclipse, allez dans le menu du haut
Source --> Générer equals() et hashCode ()
légèrement hors sujet à votre question, mais il est probablement intéressant de mentionner de toute façon:
Commons Lang a d'excellentes méthodes que vous pouvez utiliser dans overrading equals et hashcode. Découvrez EqualsBuilder.réflectionequals(...) et HashCodeBuilder.reflectionHashCode(...) . Ça m'a épargné beaucoup de maux de tête dans le passé-même si bien sûr si vous voulez juste faire "égal" sur ID il se peut que cela ne rentre pas vos circonstances.
je suis également d'accord que vous devez utiliser l'annotation @Override
chaque fois que vous êtes supérieur égal (ou toute autre méthode).
une autre solution rapide qui sauve le code boilerplate est . Il 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 égaux. Lombok est disponible en maven . Il suffit de l'ajouter avec fourni champ d'application:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.14.8</version>
<scope>provided</scope>
</dependency>
dans Android Studio est alt + insert - - - - > égale 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();
}
prendre en considération:
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'énoncé instanceOf
est souvent utilisé dans la mise en œuvre d'égaux.
c'est un piège populaire !
Le problème est que l'utilisation de instanceOf
viole la règle de symétrie:
(object1.equals(object2) == true)
si et seulement si (object2.equals(object1))
si le premier égal est vrai, et l'object2 est une instance d'une sous-classe de la classe où obj1 appartient, alors la seconde égale return false!
si la classe considérée dans laquelle ob1 appartient est déclarée comme définitive, cette le problème ne peut pas se poser, mais en général, vous devriez tester comme suit:
this.getClass() != otherObject.getClass();
sinon, retourne false, sinon le test
les champs de 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;
}