J'essaie de remplacer la méthode equals en Java. J'ai une classe People
qui a essentiellement 2 champs de données name
et age
. Maintenant, je veux remplacer la méthode equals
pour pouvoir vérifier entre les objets 2 Personnes.
Mon code est comme suit
public boolean equals(People other){
boolean result;
if((other == null) || (getClass() != other.getClass())){
result = false;
} // end if
else{
People otherPeople = (People)other;
result = name.equals(other.name) && age.equals(other.age);
} // end else
return result;
} // end equals
Mais lorsque j'écris age.equals(other.age)
, cela me donne une erreur, car la méthode Equals ne peut comparer que String et age est Integer.
J'ai utilisé l'opérateur ==
comme suggéré et mon problème est résolu.
//Written by K@stackoverflow
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
ArrayList<Person> people = new ArrayList<Person>();
people.add(new Person("Subash Adhikari", 28));
people.add(new Person("K", 28));
people.add(new Person("StackOverflow", 4));
people.add(new Person("Subash Adhikari", 28));
for (int i = 0; i < people.size() - 1; i++) {
for (int y = i + 1; y <= people.size() - 1; y++) {
boolean check = people.get(i).equals(people.get(y));
System.out.println("-- " + people.get(i).getName() + " - VS - " + people.get(y).getName());
System.out.println(check);
}
}
}
}
//written by K@stackoverflow
public class Person {
private String name;
private int age;
public Person(String name, int age){
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!Person.class.isAssignableFrom(obj.getClass())) {
return false;
}
final Person other = (Person) obj;
if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
return false;
}
if (this.age != other.age) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 3;
hash = 53 * hash + (this.name != null ? this.name.hashCode() : 0);
hash = 53 * hash + this.age;
return hash;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Sortie:
courir:
- Subash Adhikari - VS - K faux
- Subash Adhikari - VS - StackOverflow false
- Subash Adhikari - VS - Subash Adhikari true
- K - VS - StackOverflow false
- K - VS - Subash Adhikari false
- StackOverflow - VS - Subash Adhikari false
- BUILD SUCCESSFUL (temps total: 0 secondes)
L'introduction d'une nouvelle signature de méthode qui modifie les types de paramètres s'appelle surcharge:
public boolean equals(People other){
Ici, People
est différent de Object
.
Lorsqu'une signature de méthode reste identique à celle de sa super-classe, elle est appelée substitution et l'annotation @Override
permet de distinguer les deux au moment de la compilation:
@Override
public boolean equals(Object other){
Sans voir la déclaration réelle de age
, il est difficile de dire pourquoi l'erreur apparaît.
Je ne suis pas sûr des détails car vous n'avez pas posté le code entier, mais:
hashCode()
égalementequals
doit avoir le type d'argument Object
et non People
. Au moment où vous surchargez, sans remplacer, la méthode equals, ce qui n’est probablement pas ce que vous voulez, surtout que vous vérifiez son type ultérieurement.instanceof
pour vérifier qu'il s'agit bien d'un objet People, par exemple. if (!(other instanceof People)) { result = false;}
equals
est utilisé pour tous les objets, mais pas les primitives. Je pense que vous voulez dire que l'âge est une int
(primitive), auquel cas il suffit d'utiliser ==
. Notez qu'un entier (avec un "I" majuscule) est un objet qui doit être comparé avec un égal.Voir Quels problèmes doivent être pris en compte lors de la substitution de equals et hashCode en Java? pour plus de détails.
@Override
public boolean equals(Object that){
if(this == that) return true;//if both of them points the same address in memory
if(!(that instanceof People)) return false; // if "that" is not a People or a childclass
People thatPeople = (People)that; // than we can cast it to People safely
return this.name.equals(thatPeople.name) && this.age == thatPeople.age;// if they have the same name and same age, then the 2 objects are equal unless they're pointing to different memory adresses
}
Selon Effective Java , écraser la méthode
equals
semble simple, mais il existe de nombreuses façons de se tromper et les conséquences peuvent être désastreuses. Le moyen le plus simple d'éviter les problèmes consiste à ne pas remplacer la méthodeequals
, auquel cas chaque instance de la classe n'est égale qu'à elle-même. C’est la bonne chose à faire si l’une des conditions suivantes est remplie:
Chaque instance de la classe est intrinsèquement unique . Cela est vrai pour les classes telles que Thread qui représentent des entités actives plutôt que des valeurs. L'implémentation equals fournie par Object a le bon comportement pour ces classes.
Il n'est pas nécessaire que la classe fournisse un test d '"égalité logique". Par exemple, Java.util.regex.Pattern aurait pu remplacer les valeurs égales pour vérifier si deux occurrences de modèle représentaient exactement la même expression régulière, mais les concepteurs ne pensaient pas que les clients auraient besoin ou désiraient cette fonctionnalité. Dans ces circonstances, l'implémentation d'égal à égal héritée de Object est idéale.
Une superclasse a déjà remplacé les égaux, et le comportement de la superclasse est approprié pour cette classe. Par exemple, la plupart des implémentations de Set héritent de leur implémentation equals de AbstractSet, de implémentations de List de AbstractList et de map de AbstractMap.
La classe est private ou package-private , et vous êtes certain que sa méthode equals ne sera jamais invoquée. Si vous avez une aversion extrême pour le risque, vous pouvez remplacer la méthode equals pour vous assurer qu’elle n’est pas invoquée par inadvertance:
equals
implémente une relation d'équivalence. Il a ces propriétés:Réflexe: pour toute valeur de référence non nulle x
, x.equals(x)
doit renvoyer true.
Symétrique: pour toute valeur de référence non nulle, x
et y
, x.equals(y)
doivent renvoyer true si et seulement si y.equals (x) renvoie true.
Transitive: pour toute valeur de référence non nulle x
, y
, z
, si x.equals(y)
renvoie true
et y.equals(z)
renvoie true
, alors x.equals(z)
doit renvoyer true
.
Consistante: pour toute valeur de référence non nulle x
et y
, plusieurs invocations de x.equals(y)
doivent renvoyer de manière cohérente true
ou systématiquement false
, à condition qu'aucune information ne soit utilisée. dans comparaisons égales est modifié.
Pour toute valeur de référence non nulle x
, x.equals(null)
doit renvoyer false
.
Utilisez l'opérateur ==
pour vérifier si l'argument est une référence à cet objet. Si oui, retourne vrai. Il ne s’agit que d’une optimisation des performances, mais elle vaut la peine d’être effectuée si la comparaison est potentiellement coûteuse.
Utilisez l'opérateur instanceof
pour vérifier si le type de l'argument est correct. Sinon, retourne false. En règle générale, le type correct est la classe dans laquelle la méthode se produit. Parfois, il s'agit d'une interface implémentée par cette classe. Utilisez une interface si la classe implémente une interface qui affine le contrat d'égalité pour permettre des comparaisons entre les classes qui implémentent l'interface. Les interfaces de collection telles que Set, List, Map et Map.Entry ont cette propriété.
Transformez l'argument dans le type correct. Parce que cette conversion a été précédée par une instance de test, son succès est assuré.
Pour chaque champ "significatif" de la classe, vérifiez si ce champ de l'argument correspond au champ correspondant de cet objet. Si tous ces tests réussissent, renvoyez true; sinon, retourne faux. Si le type à l’étape 2 est une interface, vous devez accéder aux champs de l’argument via des méthodes d’interface; Si le type est une classe, vous pourrez peut-être accéder directement aux champs, en fonction de leur accessibilité.
Pour les champs primitifs dont le type n'est pas float
ou double
, utilisez l'opérateur ==
pour les comparaisons. pour les champs de référence d'objet, appelez la méthode equals
de manière récursive; pour les champs float
, utilisez la méthode statique Float.compare(float, float)
; et pour les champs double
, utilisez Double.compare(double, double)
. Le traitement spécial des champs float et double est rendu nécessaire par l'existence de Float.NaN
, -0.0f
et des valeurs doubles analogues; Bien que vous puissiez comparer les champs float
et double
avec les méthodes statiques Float.equals
et Double.equals
, cela impliquerait une auto-sélection de toutes les comparaisons, ce qui aurait des performances médiocres. Pour les champs array
, appliquez ces instructions à chaque élément. Si chaque élément d'un champ de tableau est significatif, utilisez l'une des méthodes Arrays.equals
.
Certains champs de référence d'objet peuvent légitimement contenir null
. Pour éviter la possibilité d'un NullPointerException
, vérifiez l'égalité de ces champs à l'aide de la méthode statique Objects.equals(Object, Object)
.
// Class with a typical equals method
public final class PhoneNumber {
private final short areaCode, prefix, lineNum;
public PhoneNumber(int areaCode, int prefix, int lineNum) {
this.areaCode = rangeCheck(areaCode, 999, "area code");
this.prefix = rangeCheck(prefix, 999, "prefix");
this.lineNum = rangeCheck(lineNum, 9999, "line num");
}
private static short rangeCheck(int val, int max, String arg) {
if (val < 0 || val > max)
throw new IllegalArgumentException(arg + ": " + val);
return (short) val;
}
@Override public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof PhoneNumber))
return false;
PhoneNumber pn = (PhoneNumber)o;
return pn.lineNum == lineNum && pn.prefix == prefix
&& pn.areaCode == areaCode;
}
... // Remainder omitted
}
Puisque je suppose que age
est de type int
:
public boolean equals(Object other){
boolean result;
if((other == null) || (getClass() != other.getClass())){
result = false;
} // end if
else{
People otherPeople = (People)other;
result = name.equals(otherPeople.name) && age == otherPeople.age;
} // end else
return result;
} // end equals
Lorsque vous comparez des objets en Java, vous faites un contrôle sémantique, en comparant le type et état d'identification des objets à:
null
Règles:
a.equals(b) == b.equals(a)
equals()
donne toujours true
ou false
, mais jamais un NullpointerException
, ClassCastException
ni aucun autre objet jetableComparaison:
instanceof
pour la comparaison de types (qui ne fonctionne que s’il n’ya pas de sous-classes et viole la règle de symétrie lorsque A extends B -> a instanceof b != b instanceof a)
.Pour votre classe Person
:
public boolean equals(Object obj) {
// same instance
if (obj == this) {
return true;
}
// null
if (obj == null) {
return false;
}
// type
if (!getClass().equals(obj.getClass())) {
return false;
}
// cast and compare state
Person other = (Person) obj;
return Objects.equals(name, other.name) && Objects.equals(age, other.age);
}
Classe d’utilité générique réutilisable:
public final class Equals {
private Equals() {
// private constructor, no instances allowed
}
/**
* Convenience equals implementation, does the object equality, null and type checking, and comparison of the identifying state
*
* @param instance object instance (where the equals() is implemented)
* @param other other instance to compare to
* @param stateAccessors stateAccessors for state to compare, optional
* @param <T> instance type
* @return true when equals, false otherwise
*/
public static <T> boolean as(T instance, Object other, Function<? super T, Object>... stateAccessors) {
if (instance == null) {
return other == null;
}
if (instance == other) {
return true;
}
if (other == null) {
return false;
}
if (!instance.getClass().equals(other.getClass())) {
return false;
}
if (stateAccessors == null) {
return true;
}
return Stream.of(stateAccessors).allMatch(s -> Objects.equals(s.apply(instance), s.apply((T) other)));
}
}
Pour votre classe Person
, à l'aide de cette classe d'utilitaire:
public boolean equals(Object obj) {
return Equals.as(this, obj, t -> t.name, t -> t.age);
}
si age est int, vous devez utiliser == s'il s'agit d'un objet Integer, vous pouvez utiliser equals (). Vous devez également implémenter la méthode hashcode si vous écrasez égal à. Les détails du contrat sont disponibles dans le javadoc d’Object et également sur diverses pages Web.