Comment remplacer la méthode equals en Java

108

J'essaye de remplacer la méthode d'égalité en Java. J'ai une classe Peoplequi a essentiellement 2 champs de données nameet age. Maintenant, je veux remplacer la equalsméthode afin de pouvoir vérifier entre 2 objets People.

Mon code est le suivant

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 quand j'écris, age.equals(other.age)cela me donne une erreur car la méthode égale ne peut comparer que la chaîne et l'âge est un entier.

Solution

J'ai utilisé l' ==opérateur comme suggéré et mon problème est résolu.

castor
la source
3
Hé que diriez-vous de this.age == other.age? :)
denis.solonenko
1
Quel est le type de données pour l'âge? int OU Integer? De plus, quelle version de JDK utilisez-vous?
Manish
2
"comme méthode égale ne peut comparer que la chaîne" - Qui vous a dit que la méthode égale ne peut comparer que la chaîne? La méthode equals appartient à la classe Object et toute classe créée aura l'implémentation égale par défaut. Vous pouvez appeler des égaux sur N'IMPORTE QUELLE classe Java
Manish

Réponses:

127
//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 (obj.getClass() != this.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;
        }
    }

Production:

courir:

- Subash Adhikari - VS - K faux

- Subash Adhikari - VS - StackOverflow false

- Subash Adhikari - VS - Subash Adhikari vrai

- K - VS - StackOverflow false

- K - VS - Subash Adhikari faux

- StackOverflow - VS - Subash Adhikari false

- CONSTRUCTION RÉUSSIE (temps total: 0 seconde)

Kim
la source
7
quelle est la hash = 53 * hashraison pour laquelle vous l'utilisez?
kittu
2
L'utilisation getClass()va poser des problèmes si la classe est sous-classée et est comparée à un objet de la super-classe.
Tuxdude
1
peut être bcoz 53 est un nombre premier , jetez un œil à cette réponse stackoverflow.com/a/27609/3425489 , a-t-il commenté en choisissant les nombres danshashCode()
Shantaram Tupe
1
La réponse gagnante à cette question explique pourquoi vous remplacez hashCode () stackoverflow.com/a/27609/1992108
Pegasaurus
7
Envisagez d'utiliser if (getClass ()! = Obj.getClass ()) ... plutôt que d'utiliser l' instanceofopérateur ou isAssignableFrom. Cela nécessitera une correspondance de type exacte, plutôt qu'une correspondance de sous-type. - Exigence symétrique. Aussi pour comparer Stringou d'autres types d'objets, vous pouvez utiliser Objects.equals(this.name,other.name).
YoYo
22

L'introduction d'une nouvelle signature de méthode qui modifie les types de paramètres s'appelle la surcharge :

public boolean equals(People other){

Voici Peopledifférent de Object.

Lorsqu'une signature de méthode reste identique à celle de sa superclasse, elle est appelée overriding et l' @Overrideannotation 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.

fortran
la source
18

Je ne suis pas sûr des détails car vous n'avez pas publié l'intégralité du code, mais:

  • n'oubliez pas de remplacer hashCode()aussi
  • la equalsméthode devrait avoir Object, pas Peoplecomme type d'argument. Pour le moment, vous surchargez, et ne remplacez pas, la méthode equals, ce qui n'est probablement pas ce que vous voulez, d'autant plus que vous vérifiez son type plus tard.
  • vous pouvez utiliser instanceofpour vérifier qu'il s'agit d'un objet People, par exempleif (!(other instanceof People)) { result = false;}
  • equalsest utilisé pour tous les objets, mais pas pour les primitives. Je pense que vous voulez dire que l'âge est un int(primitif), auquel cas il suffit d'utiliser ==. Notez qu'un entier (avec un 'I' majuscule) est un objet qui doit être comparé à des égaux.

Voir Quels problèmes doivent être pris en compte lors de la substitution de equals et hashCode en Java? pour plus de détails.

Adrian Mouat
la source
12
@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
}
NeverJr
la source
12

Item 10: Obéir au contrat général en cas de dépassement des égaux

Selon Effective Java , remplacer la equalsméthode 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 est de ne pas remplacer la equalsméthode, 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 s'applique:

  • 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 exactement 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 equals pour vérifier si deux instances Pattern représentaient exactement la même expression régulière, mais les concepteurs ne pensaient pas que les clients auraient besoin ou voudraient de cette fonctionnalité. Dans ces circonstances, l'implémentation equals héritée d'Object est idéale.

  • Une superclasse a déjà remplacé égal à, 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, des implémentations de List de AbstractList et des implémentations de Map de AbstractMap.

  • La classe est privée ou privée du package , et vous êtes certain que sa méthode equals ne sera jamais appelée. Si vous êtes extrêmement réticent au risque, vous pouvez remplacer la méthode equals pour vous assurer qu'elle n'est pas invoquée accidentellement:

La equalsméthode implémente une relation d'équivalence. Il a ces propriétés:

  • Réflexif: pour toute valeur de référence non nulle x, x.equals(x)doit retourner true.

  • Symétrique: pour toutes les valeurs de référence non nulles xet y, x.equals(y)doit retourner true si et seulement si y.equals (x) renvoie true.

  • Transitif: Pour toutes les valeurs de référence non nulles x, y, z, si les x.equals(y)retours trueet les y.equals(z)retours true, puis x.equals(z)doit revenir true.

  • Cohérent: pour toutes les valeurs de référence non nulles xet y, plusieurs invocations de x.equals(y)doivent systématiquement renvoyer trueou renvoyer systématiquement false, à condition qu'aucune information utilisée dans les comparaisons égales ne soit modifiée.

  • Pour toute valeur de référence non nulle x, x.equals(null)doit retourner false.

Voici une recette pour une méthode égale de haute qualité:

  1. Utilisez l' ==opérateur pour vérifier si l'argument est une référence à cet objet. Si tel est le cas, retournez true. Il ne s'agit que d'une optimisation des performances, mais qui en vaut la peine si la comparaison est potentiellement coûteuse.

  2. Utilisez l' instanceofopérateur pour vérifier si l'argument a le type correct. Sinon, retournez 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 égal 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é.

  3. Convertissez l'argument dans le type correct. Étant donné que cette conversion a été précédée d'un test instanceof, son succès est garanti.

  4. 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, retournez false. Si le type de l'étape 2 est une interface, vous devez accéder aux champs de l'argument via les méthodes d'interface; si le type est une classe, vous pourrez peut-être accéder directement aux champs, en fonction de leur accessibilité.

  5. Pour les champs primitifs dont le type n'est pas floatou double, utilisez l' ==opérateur pour les comparaisons; pour les champs de référence d'objet, appelez la equalsméthode de manière récursive; pour les floatchamps, utilisez la Float.compare(float, float)méthode statique ; et pour les doublechamps, utilisez Double.compare(double, double). Le traitement spécial du flotteur et double champs est rendue nécessaire par l'existence de Float.NaN, -0.0fet les valeurs doubles analogues; Bien que vous puissiez comparer floatet des doublechamps avec les méthodes statiques Float.equalset Double.equals, cela impliquerait une mise en boîte automatique sur chaque comparaison, ce qui aurait des performances médiocres. Pour les arraychamps, appliquez ces directives à chaque élément. Si chaque élément d'un champ de tableau est significatif, utilisez l'une des Arrays.equalsméthodes.

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

la source
1
N'oubliez pas de mentionner que vous devez également passer outre hashCode(). A noter également, que depuis l' écriture java7 equals()et hashCode()méthodes est devenu beaucoup plus facile à l'aide Objects.equals(), Arrays.equals()et Objects.hashCode(), Arrays.hashCode().
Arnold Schrijver
3
Envisagez d'utiliser if (getClass() != obj.getClass()) ...plutôt que d'utiliser l'opérateur instanceof. Cela nécessitera une correspondance de type exacte , plutôt qu'une correspondance de sous-type. - Exigence symétrique.
YoYo
@YoYo est correct ... l'utilisation de instanceof pourrait faire échouer la propriété symétrique. Si o est une sous-classe de PhoneNumber comme peut-être PhoneNumberWithExtension, et qu'il remplace égal à la même manière en utilisant instanceof, alors o.equals (this) échouerait le test instanceof tandis que PhoneNumber.equals le passerait et retournerait true (en supposant que tous les autres champs PhoneNumber sont égaux).
ldkronos
5

Puisque je suppose que agec'est du 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
Luchian Grigore
la source
Cela se traduira par un NullPointerExceptionsi nameest null.
orien
@orien Ce n'est pas grave, c'est peut-être dans le contrat qu'on n'attribue namejamais de nullvaleur ...
fortran
@fortran Alors ... peut-être que ce n'est pas grave;)
orien
5

Lorsque vous comparez des objets en Java, vous effectuez une vérification sémantique , comparant le type et l’état d’identification des objets à:

  • lui-même (même instance)
  • lui-même (clone ou copie reconstruite)
  • d'autres objets de types différents
  • autres objets du même type
  • null

Règles:

  • Symétrie :a.equals(b) == b.equals(a)
  • equals()cède toujours trueou false, mais jamais un NullpointerException, ClassCastExceptionou tout autre jetable

Comparaison:

  • Vérification de type : les deux instances doivent être du même type, ce qui signifie que vous devez comparer les classes réelles pour l'égalité. Cela n'est souvent pas correctement implémenté, lorsque les développeurs utilisent instanceofpour la comparaison de types (qui ne fonctionne que tant qu'il n'y a pas de sous-classes et enfreint la règle de symétrie quand A extends B -> a instanceof b != b instanceof a).
  • Vérification sémantique de l'état d'identification : assurez-vous de comprendre par quel état les instances sont identifiées. Les personnes peuvent être identifiées par leur numéro de sécurité sociale, mais pas par la couleur de leurs cheveux (peuvent être teints), leur nom (peut être changé) ou l'âge (change tout le temps). Vous ne devez comparer l'état complet qu'avec les objets de valeur (tous les champs non transitoires), sinon ne vérifiez que ce qui identifie l'instance.

Pour votre Personclasse:

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 Personclasse, en utilisant cette classe utilitaire:

public boolean equals(Object obj) {
    return Equals.as(this, obj, t -> t.name, t -> t.age);
}
Peter Walser
la source
1

si age est int, vous devez utiliser == s'il s'agit d'un objet Integer alors vous pouvez utiliser equals (). Vous devez également implémenter la méthode de hashcode si vous remplacez equals. Les détails du contrat sont disponibles dans le javadoc d'Object et également sur diverses pages Web.

Ashwinee K Jha
la source
0

Voici la solution que j'ai récemment utilisée:

public class Test {
    public String a;
    public long b;
    public Date c;
    public String d;
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Test)) {
            return false;
        }
        Test testOther = (Test) obj;
        return (a != null ? a.equals(testOther.a) : testOther.a == null)
                && (b == testOther.b)
                && (c != null ? c.equals(testOther.c) : testOther.c == null)
                && (d != null ? d.equals(testOther.d) : testOther.d == null);
    }

}
SSharma
la source