Question

http://leepoint.net/notes-java/data/expressions /22compareobjects.html

  

Il se avère que la définition equals ()   n'est pas trivial; en fait, il est modérément   dur pour obtenir ce droit, en particulier dans   le cas des sous-classes. Le meilleur   le traitement des questions est   Noyau Java Horstmann Vol 1.

Si égaux () doivent toujours être surchargées, alors ce qui est une bonne approche pour ne pas être acculé à avoir à faire la comparaison d'objets? Quelles sont les bonnes alternatives "design"?

EDIT:

Je ne suis pas sûr que cela vient d'en face que je l'avais prévu. Peut-être que la question devrait être plus le long des lignes de « Pourquoi voudriez-vous comparer deux objets? » Sur la base de votre réponse à cette question, est-il une solution alternative à la comparaison? Je ne veux pas, une autre implémentation d'égal à égal. Je veux dire, ne pas utiliser l'égalité du tout. Je pense que le point essentiel est de commencer par cette question, pourquoi voudriez-vous comparer les deux objets.

Était-ce utile?

La solution

  

Si égaux () doivent toujours être surchargées,   alors ce qui est une bonne approche pour ne pas   étant acculé à avoir à faire   comparaison d'objets?

Vous vous trompez. Vous devez passer outre égale le plus rarement possible.


Toutes ces informations provient de Effective Java, de deuxième édition ( Josh Bloch ). Le premier chapitre de l'édition sur ce point est toujours disponible en tant que href="http://developer.java.sun.com/developer/Books/effectivejava/Chapter3.pdf" gratuit .

De Effective Java:

  

La meilleure façon d'éviter les problèmes est   de ne pas surcharger la méthode d'égal à égal, dans   auquel cas chaque instance de la classe   est égal qu'à elle-même.

Le problème avec dépassement arbitraire égaux / hashCode est l'héritage. Certains tests, il est égal défenseur des mises en œuvre comme ceci:

if (this.getClass() != other.getClass()) {
    return false; //inequal
}

En fait, le Eclipse (3.4) éditeur Java ne vient quand vous générez la méthode utilisant la outils source. Selon Bloch, c'est une erreur car elle viole le principe de substitution Liskov .

De Effective Java:

  

Il n'y a pas moyen d'étendre un   classe instanciable et ajouter une valeur   composante tout en préservant les égaux   contrat.

Deux façons de minimiser les problèmes d'égalité sont décrits dans les Classes et interfaces chapitre:

  1. composition Faveur sur l'héritage
  2. Conception et documents pour l'héritage ou bien interdire

Pour autant que je peux voir, la seule alternative est de tester l'égalité dans une forme extérieure à la classe, et comment cela serait réalisé dépendra de la conception du type et le contexte que vous essayez de l'utiliser dans.

Par exemple, vous pouvez définir une interface qui documente la façon dont il était à comparer. Dans le code ci-dessous, les instances de service peuvent être remplacées lors de l'exécution avec une version plus récente de la même classe - dans ce cas, ayant des classloaders, équivaut à des comparaisons seraient toujours retourner faux, donc primordial égal / hashCode serait redondant

.
public class Services {

    private static Map<String, Service> SERVICES = new HashMap<String, Service>();

    static interface Service {
        /** Services with the same name are considered equivalent */
        public String getName();
    }

    public static synchronized void installService(Service service) {
        SERVICES.put(service.getName(), service);
    }

    public static synchronized Service lookup(String name) {
        return SERVICES.get(name);
    }
}

  

« Pourquoi voudriez-vous comparer deux objets? »

L'exemple évident est de tester si deux chaînes sont les mêmes (ou deux fichiers ou URIs). Par exemple, si vous vouliez construire un ensemble de fichiers à analyser. Par définition, l'ensemble ne contient que des éléments uniques. Set de type repose sur les égaux / méthodes hashCode pour garantir l'unicité de ses éléments.

Autres conseils

Je ne pense pas qu'il est vrai que égaux doivent toujours être surchargées. La règle que je comprends, il est primordial que égale n'a de sens que dans le cas où vous êtes clair sur la façon de définir des objets sémantiquement équivalents. Dans ce cas, vous remplacer hashCode () et de sorte que vous n'avez pas des objets que vous avez défini comme différents hashcodes retour équivalent.

Si vous ne pouvez pas définir l'équivalence de sens, je ne vois pas l'avantage.

Qu'en est-il juste de le faire droit?

Voici mon égal modèle qui est appliqué à partir de la connaissance efficace Java par Josh Bloch. Lire le livre pour plus de détails:

@Override
public boolean equals(Object obj) {
    if(this == obj) {
        return true;
    }

    // only do this if you are a subclass and care about equals of parent
    if(!super.equals(obj)) {
        return false;
    }
    if(obj == null || getClass() != obj.getClass()) {
        return false;
    }
    final YourTypeHere other = (YourTypeHere) obj;
    if(!instanceMember1.equals(other.instanceMember1)) {
       return false;
     }
     ... rest of instanceMembers in same pattern as above....
     return true;
 }

mmhh

Dans certains cas, vous pouvez faire l'objet non modifiable (lecture seule) et l'avoir créé à partir d'un seul point (une méthode de fabrication)

Si deux objets avec les mêmes données d'entrée (paramètres de création) sont nécessaires l'usine retourne la même instance ref, puis en utilisant « == » serait suffisant.

Cette approche est utile dans certaines circonstances seulement. Et la plupart du temps regarderait surpuissant.

Jetez un oeil à cette réponse pour savoir comment mettre en œuvre une telle chose.

Attention il y a beaucoup de code

Pour voir comment court le fonctionnement de la classe wrapper depuis java 1.5

Integer a = Integer.valueOf( 2 );
Integer b = Integer.valueOf( 2 );

a == b 

est vrai en

new Integer( 2 ) == new Integer( 2 )  

est faux.

Il maintient en interne la référence et le retourner si la valeur d'entrée est le même.

Comme vous le savez entier est en lecture seule

Quelque chose de semblable se produit avec la classe String à partir de laquelle cette question était sur le point.

Peut-être que je manque le point, mais la seule raison d'utiliser des égaux au lieu de définir votre propre méthode avec un autre nom est parce que beaucoup des collections (et probablement d'autres choses dans le JDK ou quelque chose comme ça ces jours-ci) attendent la méthode equals pour définir un résultat cohérent. Mais au-delà, je peux penser à trois types de comparaisons que vous voulez faire en égaux:

  1. Les deux objets vraiment sont les même instance. Cela n'a aucun sens d'utiliser est égal parce que vous pouvez utiliser ==. En outre, et corrigez-moi si je l'ai oublié comment cela fonctionne en Java, la valeur par défaut est égale à la méthode le fait en utilisant les codes de hachage générés automatiquement.
  2. Les deux objets ont des références aux mêmes instances, mais ne sont pas la même instance. Ceci est utile, euh, parfois ... surtout si elles sont objets persistants et se réfèrent au même objet dans le DB. Vous devez définir votre méthode equals pour le faire.
  3. Les deux objets ont des références à des objets qui sont égaux en valeur, mais ils peuvent ou ne peuvent pas être les mêmes instances (en d'autres termes, vous comparer des valeurs tout au long de la hiérarchie).

Pourquoi voudriez-vous comparer deux objets? Eh bien, si elles sont égales, vous voulez faire une chose, et si elles ne sont pas, vous voulez faire autre chose.

Cela dit, cela dépend de l'espèce.

La principale raison de passer outre equals () dans la plupart des cas est de vérifier les doublons dans certaines collections. Par exemple, si vous souhaitez utiliser un ensemble pour contenir un objet que vous avez créé, vous devez redéfinir equals () et hashCode () dans votre objet. De même si vous souhaitez utiliser votre objet personnalisé comme une clé dans une carte.

Cela est essentiel comme je l'ai vu beaucoup de gens font l'erreur dans la pratique d'ajouter leurs objets personnalisés à des ensembles ou des cartes sans écraser equals () et hashCode (). La raison pour laquelle cela peut être particulièrement insidieux est le compilateur ne se plaindra pas et vous pouvez vous retrouver avec plusieurs objets qui contiennent les mêmes données mais ont des références dans une collection qui ne permet pas de doublons.

Par exemple, si vous aviez un haricot simple appelé NameBean avec un seul attribut String « name », vous pouvez construire deux instances de NameBean (par exemple nom1 et nom2), chacun avec la même valeur d'attribut « name » (par exemple, « Alice » ). Vous pouvez ensuite ajouter à la fois nom1 et nom2 à un ensemble et l'ensemble serait la taille 2 plutôt que la taille 1 qui est ce qui est prévu. De même si vous avez une carte telle que la carte afin de cartographier le grain de nom à un autre objet, et vous devez d'abord cartographié nom1 à la chaîne « première » et cartographié plus tard nom2 à la chaîne « deuxième », vous aurez les deux paires clé / valeur dans la carte (par exemple nom1 -> "premier", name2 -> "seconde"). Ainsi, lorsque vous faites une carte lookup il renvoie la valeur mis en correspondance avec la référence exacte que vous passez, qui est soit nom1, nom2, ou une autre référence avec le nom « Alice » qui renverra nulle.

Voici un exemple concret précédé par la sortie de l'exécuter:

Sortie:

Adding duplicates to a map (bad):
Result of map.get(bean1):first
Result of map.get(bean2):second
Result of map.get(new NameBean("Alice"): null

Adding duplicates to a map (good):
Result of map.get(bean1):second
Result of map.get(bean2):second
Result of map.get(new ImprovedNameBean("Alice"): second

Code:

// This bean cannot safely be used as a key in a Map
public class NameBean {
    private String name;
    public NameBean() {
    }
    public NameBean(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return name;
    }
}

// This bean can safely be used as a key in a Map
public class ImprovedNameBean extends NameBean {
    public ImprovedNameBean(String name) {
        super(name);
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if(obj == null || getClass() != obj.getClass()) {
            return false;
        }
        return this.getName().equals(((ImprovedNameBean)obj).getName());
    }
    @Override
    public int hashCode() {
        return getName().hashCode();
    }
}

public class MapDuplicateTest {
    public static void main(String[] args) {
        MapDuplicateTest test = new MapDuplicateTest();
        System.out.println("Adding duplicates to a map (bad):");
        test.withDuplicates();
        System.out.println("\nAdding duplicates to a map (good):");
        test.withoutDuplicates();
    }
    public void withDuplicates() {
        NameBean bean1 = new NameBean("Alice");
        NameBean bean2 = new NameBean("Alice");

        java.util.Map<NameBean, String> map
                = new java.util.HashMap<NameBean, String>();
        map.put(bean1, "first");
        map.put(bean2, "second");
        System.out.println("Result of map.get(bean1):"+map.get(bean1));
        System.out.println("Result of map.get(bean2):"+map.get(bean2));
        System.out.println("Result of map.get(new NameBean(\"Alice\"): "
                + map.get(new NameBean("Alice")));
    }
    public void withoutDuplicates() {
        ImprovedNameBean bean1 = new ImprovedNameBean("Alice");
        ImprovedNameBean bean2 = new ImprovedNameBean("Alice");

        java.util.Map<ImprovedNameBean, String> map
                = new java.util.HashMap<ImprovedNameBean, String>();
        map.put(bean1, "first");
        map.put(bean2, "second");
        System.out.println("Result of map.get(bean1):"+map.get(bean1));
        System.out.println("Result of map.get(bean2):"+map.get(bean2));
        System.out.println("Result of map.get(new ImprovedNameBean(\"Alice\"): "
                + map.get(new ImprovedNameBean("Alice")));
    }
}

L'égalité est fondamentale à la logique (voir loi d'identité), et il n'y a pas beaucoup de programmation, vous pouvez le faire sans elle. En ce qui concerne la comparaison des instances de classes que vous écrivez, et c'est à vous. Si vous devez être en mesure de les trouver dans les collections ou les utiliser comme clés dans les cartes, vous aurez besoin des contrôles à l'égalité.

Si vous avez écrit plus de quelques bibliothèques triviaux en Java, vous saurez que l'égalité est difficile à obtenir le droit, surtout quand les seuls outils de la poitrine sont et equals hashCode. L'égalité finit par être couplée étroitement avec des hiérarchies de classes, ce qui rend pour le code cassant. De plus, aucune vérification de type est fourni, car ces méthodes prennent juste des paramètres de type objet.

Il y a une façon de faire de l'égalité de contrôle (et hashing) beaucoup moins et plus de type sécurisé sujette à erreur. Dans le fonctionnelle bibliothèque Java , vous trouverez Equal<A> (et correspondant Hash<A> ) où l'égalité est découplé en une seule classe. Il a des méthodes pour la composition des instances pour vos Equal cours d'instances existantes, ainsi que les emballages des collections, Iterables , HashMap et HashSet , que l'utilisation et <=> au lieu de <=> et <=> <=>.

Qu'est-ce qui est le mieux au sujet de cette approche est que vous ne pouvez jamais oublier d'écrire égaux et la méthode de hachage quand ils sont appelés pour. Le système de type vous aidera à mémoriser.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top