Pourquoi .compareTo () dans une interface alors que .equals () est dans une classe en Java?

30

Je veux savoir pourquoi .compareTo()est dans l' Comparableinterface alors qu'une méthode comme .equalsest dans la Objectclasse. Pour moi, il semble arbitraire pourquoi une méthode comme .compareTo()n'est pas Objectdéjà dans la classe.

Pour l'utiliser .compareTo(), vous implémentez l' Comparableinterface et implémentez la .compareTo()méthode à vos fins. Pour la .equals()méthode, vous remplacez simplement la méthode dans votre classe, car toutes les classes héritent de la Objectclasse.

Ma question est pourquoi une méthode est-elle comme .compareTo()dans une interface que vous implémentez plutôt que dans une classe comme Object? De même, pourquoi la .equals()méthode dans la classe Objectet non dans une interface doit-elle être implémentée?

Wesley
la source
2
C'est un choix de conception du langage Java (ne signifie pas nécessairement que c'était le bon choix). Dans d'autres langues, par exemple Haskell , vous devez implémenter l'interface d'égalité pour obtenir l'égalité de valeur (en fait, vous fournissez une instance à la Eqclasse de types).
mucaho

Réponses:

58

Tous les objets ne peuvent pas être comparés, mais tous les objets peuvent être vérifiés pour leur égalité. Si rien d'autre, on peut voir si deux objets existent au même endroit en mémoire (égalité de référence).

Qu'est-ce que cela signifie pour compareTo()deux Threadobjets? Comment un fil est-il «supérieur à» un autre? Comment comparez-vous deux ArrayList<T>s?

Le Objectcontrat s'applique à toutes les classes Java. Si même une classe ne peut pas être comparée à d'autres instances de sa propre classe, elle Objectne peut pas exiger qu'elle fasse partie de l'interface.

Joshua Bloch utilise les mots clés "ordre naturel" pour expliquer pourquoi une classe pourrait vouloir implémenter Comparable. Toutes les classes n'ont pas un ordre naturel comme je l'ai mentionné dans mes exemples ci-dessus, donc toutes les classes ne doivent Comparablepas implémenter ni Objectavoir la compareTométhode.

... la compareTométhode n'est pas déclarée dans Object. ... Son caractère est similaire à celui de Objectla equalsméthode, sauf qu'il permet des comparaisons d'ordres en plus des comparaisons d'égalité simples, et il est générique. En implémentant Comparable, une classe indique que ses instances ont un ordre naturel .

Java efficace, deuxième édition : Joshua Bloch. Point 12, page 62. Les points de suspension suppriment les références à d'autres chapitres et exemples de code.

Pour les cas où vous ne voulez imposer un ordre sur une non Comparableclasse qui ne dispose pas d' un ordre naturel, vous pouvez toujours fournir un Comparatorexemple pour aider les trier.


la source
3
Il y a encore plus de plaisir lorsque vous commencez à penser à compareTo avec exception (qui n'est pas une classe abstraite et l'aurait donc implémentée, ce qui signifierait que aNullPointerException.compareTo (anUnsupportedFlavorException) aurait ... ce sens?
10
tous les objets peuvent être vérifiés pour l'égalité En Java oui, mais en général non. Il y a quelques exemples d'objets où la comparaison d'égalité n'a pas de sens (pas même l'égalité de référence) - par exemple les singletons. Il pourrait y avoir des interfaces (classes abstraites) comme ValueEquality et ReferenceEquality. Ce n'est peut-être même pas une si mauvaise idée ...
qbd
5
"tous les objets peuvent être vérifiés pour leur égalité. Si rien d'autre, on peut voir si deux objets existent au même emplacement en mémoire (égalité de référence)." - puisque nous avons ==pour ce dernier, cela a un anneau creux. Sans tenir compte de la valeur par défaut redondante, on peut trouver des raisons valables de ne pas supposer equalssur toutes les classes, car tous les types ne peuvent pas prendre en charge une relation d'équivalence.
Raphael
3
Deux exemples de types où il n'est pas judicieux de définir l'égalité: les flux (par exemple les listes paresseuses potentiellement infinies ou les nombres de précision infinis) et les fonctions. Le premier a le problème que l'établissement de l'égalité peut nécessiter une comparaison à l'infini. Décider si deux fonctions sont égales est indécidable. Demander si deux instances de ces types existent dans le même emplacement mémoire est 1) pas très utile et 2) permet aux clients d'écrire du code sensible à ce qui devrait être les détails de l'implémentation. Aujourd'hui, je peux vous donner une nouvelle instance de la même liste infinie chaque fois que vous demandez, demain je pourrai la mémoriser.
Doval
6
@Snowman Le fait qu'il soit inutile combiné avec le fait d'exposer les détails de l'implémentation est une raison suffisante pour ne pas le permettre. Presque toutes les classes "basées sur des valeurs" dans Java 8 ont un mot d'ordre disant "Nous ne sommes pas responsables de ce qui se passe si vous utilisez ==" parce que la façon dont ces classes sont instanciées est un détail d'implémentation mais le langage le rend impossible à masquer. On pourrait dire que quiconque compare deux Integers par référence est un idiot, mais permettre à la comparaison de commencer est encore plus stupide.
Doval
8

Le JLS §4.3.2 définit l' classobjet de la manière suivante:

4.3.2. L'objet de classe

La classe Objectest une superclasse (§8.1.4) de toutes les autres classes.

Tous les types de classe et de tableau héritent (§8.4.8) des méthodes de classe Object, qui se résument comme suit:

  • La méthode cloneest utilisée pour créer un doublon d'un objet.

  • La méthode equalsdéfinit une notion d'égalité d'objet, qui est basée sur la comparaison de la valeur et non de la référence.

  • La méthode finalizeest exécutée juste avant la destruction d'un objet (§12.6).

  • La méthode getClassrenvoie l'objet Class qui représente la classe de l'objet.

  • Un Classobjet existe pour chaque type de référence. Il peut être utilisé, par exemple, pour découvrir le nom complet d'une classe, ses membres, sa superclasse immédiate et toutes les interfaces qu'elle implémente.

    Le type d'une expression d'appel de méthode getClassest Class<? extends |T|>Test la classe ou l'interface recherchée (§15.12.1) getClass.

    Une méthode de classe déclarée synchronized(§8.4.3.6) se synchronise sur le moniteur associé à l'objet Class de la classe.

  • La méthode hashCodeest très utile, avec la méthode égale, dans des tables de hachage telles que java.util.Hashmap.

  • Les méthodes wait, notifyet notifyAllsont utilisées dans la programmation simultanée à l'aide de threads (§17.2).

  • La méthode toStringrenvoie une représentation String de l'objet.

C'est pourquoi equalsc'est dans Objectmais compareTodans une interface séparée. Je suppose qu'ils voulaient garder Objectle minimum possible. Ils ont probablement pensé que presque tous Objects auraient besoin equalset hashCode(ce qui n'est vraiment qu'une forme de test d'égalité), mais tous les objets n'auraient pas besoin d'avoir un concept de classement , ce qui compareToest utilisé.

durron597
la source
Je suppose qu'il pourrait théoriquement y avoir une interface Equitable<T>mais si Objectelle était implémentée, alors chaque classe serait un Equitable<Object>. À ce stade, y a-t-il une différence? En effet pas vraiment, en complexité oui.
Captain Man
1
@CaptainMan En .Net, objecta Equals(object), tout comme en Java, mais il y a aussi l' IEquatable<T>interface. Bien que la raison principale de son existence soit d'éviter la boxe lorsqu'il Ts'agit d'un type de valeur, ce qui n'est pas possible en Java.
svick
hashCode n'est pas une forme de test d'égalité, car il y a des collisions de hachage. si A et B sont égaux, ils ont le même hashCode, mais si A et B ont le même hashCode, cela ne signifie pas qu'ils sont égaux!
Josef
en fait, votre réponse gagnerait grandement à passer à un JLS plus ancien ( titanium.cs.berkeley.edu/doc/java-langspec-1.0.pdf ) - il a une bien meilleure citation sur la raison pour laquelle il equalsest déclaré Objectdirectement: The methods equals and hashCode are declared for the benefit of hashtables such as java.util.Hashtable (§21.7)- en tant que conception Java est rétrocompatible, le choix de conception Java 1.0 est la véritable raison d' equalsêtre là où il est.
vaxquis
car la modification que j'ai proposée sera probablement rejetée pour être "trop ​​drastique", au cas où vous voudriez simplement Ctrl + C / Ctrl + V les éléments pertinents dans votre réponse: pastebin.com/8c4EpLRX
vaxquis
2

En plus de l'excellente réponse de Snowman, rappelez-vous qu'il s'agit d' Comparableune interface générique depuis longtemps. Un type n'implémente pas compareTo(object), il implémente compareTo(T)Test son propre type. Cela ne peut pas être implémenté object, car objectne connaît pas la classe qui en sera dérivée.

objectaurait pu définir une compareTo(object)méthode, mais cela aurait permis non seulement ce que Snowman souligne, une comparaison entre deux ArrayList<T>s ou entre deux Threads, mais même une comparaison entre un ArrayList<T>et un Thread. C'est encore plus absurde.

hvd
la source
0

Supposons que j'ai deux références d'objet: X identifie une instance de Stringcontenu "George"; Y identifie l'instance de Pointmaintien des coordonnées [12,34]. Considérez les deux questions suivantes:

  • X et Y identifient-ils des objets équivalents?

  • Est-ce que X doit trier avant, après ou équivalent à Y?

Le fait que X et Y identifient des instances de types non liés ne pose aucun problème lors de l'examen de la première question. Les objets ne peuvent être considérés comme équivalents que si leurs types partagent une base commune qui les définit comme équivalents; puisque Stringet Pointn'ayant pas une telle base (leur seul type de base commun considère tous les objets distincts comme non équivalents) la réponse est simplement "non".

Le fait que les types ne soient pas liés, cependant, pose un énorme problème en ce qui concerne la deuxième question. Certains types définissent les relations de commande entre leurs instances, et certaines relations de commande peuvent même étendre sur plusieurs types [par exemple , il serait possible BigIntegeret BigDecimalde définir des méthodes de comparaison qui permettraient des instances de chaque type à être classés par rapport aux instances de l'autre], mais il n'est généralement pas possible de prendre deux instances arbitraires et de demander "Doit X trier avant, après ou équivalent à Y" et dériver un classement total. Il serait possible de demander "Si X doit trier avant, après, équivalent ou non classé par rapport à Y" si les objets étaient tenus de signaler un ordre cohérent mais pas un totalun, mais la plupart des algorithmes de tri nécessitent des commandes totales. Ainsi, même si tous les objets pouvaient implémenter une compareTométhode si "non classé" était un retour valide, une telle méthode ne serait pas suffisamment utile pour justifier son existence.

supercat
la source