web-dev-qa-db-fra.com

Comment remplacer la méthode equals dans Java

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.

Solution

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

96
user702026
//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)

115
Kim

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.

21
fortran

Je ne suis pas sûr des détails car vous n'avez pas posté le code entier, mais:

  • n'oubliez pas de remplacer hashCode() également
  • la méthode equals 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.
  • vous pouvez utiliser 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.

17
Adrian Mouat
@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
}
10
NeverJr

Point 10: Obéir au contrat général en cas d'égalité

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éthode equals, 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:

La méthode 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.

Voici une recette pour une méthode d'égale qualité:

  1. 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.

  2. 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é.

  3. 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é.

  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, 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é.

  5. 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.

  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
    
    }
    
9
user8389458

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
5
Luchian Grigore

Lorsque vous comparez des objets en Java, vous faites un contrôle sémantique, en comparant le type et état d'identification des objets à:

  • lui-même (même instance)
  • lui-même (copie ou copie reconstituée)
  • autres objets de types différents
  • autres objets du même type
  • null

Règles:

  • Symétrie: a.equals(b) == b.equals(a)
  • equals() donne toujours true ou false, mais jamais un NullpointerException, ClassCastException ni aucun autre objet jetable

Comparaison:

  • Vérification du type: les deux instances doivent être du type identique, ce qui signifie que vous devez comparer les classes réelles pour assurer leur égalité. Cela n’est souvent pas correctement implémenté lorsque les développeurs utilisent 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).
  • Vérification sémantique de l'état d'identification: Assurez-vous de bien comprendre dans 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 (peut être teintée), leur nom (peut être changé) ou leur âge (changées tout le temps). Si vous comparez l’état complet (avec tous les champs non transitoires), cochez uniquement ce qui identifie l’instance.

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);
}
4
Peter Walser

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.

1
Ashwinee K Jha