Je réfléchis depuis un moment pourquoi Java et C # (et je suis sûr que d'autres langages) par défaut font référence à l'égalité pour ==
.
Dans la programmation que je fais (qui n'est certainement qu'un petit sous-ensemble de problèmes de programmation), je veux presque toujours l'égalité logique lors de la comparaison d'objets au lieu de l'égalité de référence. J'essayais de penser pourquoi ces deux langues ont choisi cette voie au lieu de l'inverser et d'avoir ==
une égalité logique et une égalité .ReferenceEquals()
de référence.
De toute évidence, l'utilisation de l'égalité de référence est très simple à mettre en œuvre et donne un comportement très cohérent, mais il ne semble pas que cela cadre bien avec la plupart des pratiques de programmation que je vois aujourd'hui.
Je ne souhaite pas sembler ignorer les problèmes liés à l'implémentation d'une comparaison logique, et qu'elle doit être implémentée dans chaque classe. Je me rends également compte que ces langues ont été conçues il y a longtemps, mais la question générale demeure.
Y a-t-il un avantage majeur à faire défaut à cela que je manque simplement, ou semble-t-il raisonnable que le comportement par défaut soit l'égalité logique, et par défaut à l'égalité de référence, une égalité logique n'existe pas pour la classe?
la source
Equals()
, cela ne change pas automatiquement le comportement de==
?Réponses:
C # le fait parce que Java l'a fait. Java l'a fait car Java ne prend pas en charge la surcharge des opérateurs. Étant donné que l'égalité des valeurs doit être redéfinie pour chaque classe, il ne peut pas s'agir d'un opérateur, mais doit plutôt être une méthode. OMI, c'était une mauvaise décision. Il est beaucoup plus facile à la fois d'écrire et de lire
a == b
quea.equals(b)
, et beaucoup plus naturel pour les programmeurs ayant une expérience en C ou C ++, maisa == b
c'est presque toujours faux. Les bogues liés à l'utilisation de l'==
emplacement.equals
requis ont fait perdre des milliers d'heures de programmation.la source
==
pour de nombreuses classes et il y a quelques mois, j'ai découvert que quelques développeurs ne savaient pas ce qui se==
passait réellement. Il y a toujours ce risque lorsque la sémantique d'une construction n'est pas évidente. Laequals()
notation me dit que j'utilise une méthode personnalisée et que je dois la chercher quelque part. Bottom line: Je pense que la surcharge des opérateurs est un problème ouvert en général.+
par exemple, qui fait l'addition (de valeurs numériques) et la concaténation de chaînes en même temps.a == b
être plus naturel pour les programmeurs ayant une expérience C, puisque C ne prend pas en charge la surcharge d'opérateur définie par l'utilisateur? (Par exemple, la façon C de comparer les chaînes eststrcmp(a, b) == 0
nona == b
.)char *
. Il me semble évident que comparer deux pointeurs pour l'égalité n'est pas la même chose qu'une comparaison de chaînes.La réponse courte: la cohérence
Pour répondre correctement à votre question, je suggère que nous prenions un pas en arrière et examinions la question de savoir ce que signifie l'égalité dans un langage de programmation. Il existe au moins TROIS possibilités différentes, qui sont utilisées dans différentes langues:
Ces trois types d'égalité sont souvent utilisés car ils sont pratiques à mettre en œuvre: les trois vérifications d'égalité peuvent facilement être générées par un compilateur (dans le cas d'une égalité profonde, le compilateur peut avoir besoin d'utiliser des bits de balise pour éviter les boucles infinies si une structure à être comparé a des références circulaires). Mais il y a un autre problème: aucun de ceux-ci pourrait être approprié.
Dans les systèmes non triviaux, l'égalité des objets est souvent définie comme quelque chose entre l'égalité profonde et l'égalité de référence. Pour vérifier si nous voulons considérer deux objets comme égaux dans un certain contexte, nous pourrions exiger que certains attributs soient comparés par où ils se trouvent dans la mémoire et d'autres par une égalité profonde, tandis que certains attributs peuvent être autorisés à être quelque chose de complètement différent. Ce que nous aimerions vraiment, c'est un «quatrième type d'égalité», vraiment sympa, souvent appelé égalité sémantique dans la littérature . Les choses sont égales si elles sont égales, dans notre domaine. =)
Nous pouvons donc revenir à votre question:
Que voulons-nous dire lorsque nous écrivons «a == b» dans n'importe quelle langue? Idéalement, cela devrait toujours être le même: l'égalité sémantique. Mais ce n'est pas possible.
L'une des principales considérations est que, au moins pour les types simples comme les nombres, nous nous attendons à ce que deux variables soient égales après attribution de la même valeur. Voir ci-dessous:
Dans ce cas, nous nous attendons à ce que «a est égal à b» dans les deux déclarations. Tout le reste serait fou. La plupart (sinon toutes) des langues suivent cette convention. Par conséquent, avec des types simples (ou valeurs), nous savons comment atteindre l'égalité sémantique. Avec les objets, cela peut être quelque chose de complètement différent. Voir ci-dessous:
Nous nous attendons à ce que le premier «si» soit toujours vrai. Mais qu'attendez-vous du second «si»? Ça dépend vraiment. 'DoSomething' peut-il changer l'égalité (sémantique) de a et b?
Le problème de l'égalité sémantique est qu'elle ne peut pas être générée automatiquement par le compilateur pour les objets, ni évidente à partir des affectations . Un mécanisme doit être fourni à l'utilisateur pour définir l'égalité sémantique. Dans les langages orientés objet, ce mécanisme est une méthode héritée: equals . En lisant un morceau de code OO, nous ne nous attendons pas à ce qu'une méthode ait la même implémentation exacte dans toutes les classes. Nous sommes habitués à l'héritage et à la surcharge.
Avec les opérateurs, cependant, nous nous attendons au même comportement. Lorsque vous voyez 'a == b', vous devriez vous attendre au même type d'égalité (des 4 ci-dessus) dans toutes les situations. Ainsi, dans un souci de cohérence, les concepteurs de langages ont utilisé l'égalité de référence pour tous les types. Cela ne devrait pas dépendre du fait qu'un programmeur a remplacé une méthode ou non.
PS: Le langage Dee est légèrement différent de Java et C #: l'opérateur égal signifie égalité superficielle pour les types simples et égalité sémantique pour les classes définies par l'utilisateur (la responsabilité de la mise en œuvre de l'opération = incombant à l'utilisateur - aucune valeur par défaut n'est fournie). Comme pour les types simples, l'égalité superficielle est toujours l'égalité sémantique, le langage est cohérent. Le prix à payer, cependant, est que l'opérateur égal n'est pas défini par défaut pour les types définis par l'utilisateur. Vous devez le mettre en œuvre. Et, parfois, c'est juste ennuyeux.
la source
When you see ‘a == b’ you should expect the same type of equality (from the 4 above) in all situations.
Les concepteurs de langage de Java ont utilisé l'égalité de référence pour les objets et l'égalité sémantique pour les primitives. Ce n'est pas évident pour moi que c'était la bonne décision, ou que cette décision est plus "cohérente" que==
de se laisser surcharger pour l'égalité sémantique des objets.a
etb
sont du même type, l'expressiona==b
teste sia
etb
détient la même chose. Si l'un d'eux contient une référence à l'objet # 291, et l'autre contient une référence à l'objet # 572, ils ne contiennent pas la même chose. Le contenu des objets # 291 et # 572 peut être équivalent, mais les variables elles-mêmes contiennent des choses différentes.a == b
et savoir ce qu'il fait. De même, vous pouvez voira.equals(b)
et présumer qu'une surchargeequals
. S'il s'agit d'a == b
appelsa.equals(b)
(s'ils sont mis en œuvre), s'agit-il d'une comparaison par référence ou par contenu? Tu ne te souviens pas? Vous devez vérifier la classe A. Le code n'est plus aussi rapide à lire si vous n'êtes même pas sûr de ce qui est appelé. Ce serait comme si des méthodes avec la même signature étaient autorisées, et la méthode appelée dépend de la portée actuelle. De tels programmes seraient impossibles à lire.Parce que cette dernière approche serait source de confusion. Considérer:
Ce code
"ok"
doit-il s'imprimer ou doit-il lancer unNullPointerException
?la source
Pour Java et C #, l'avantage réside dans le fait qu'ils sont orientés objet.
Du point de vue des performances - le code plus facile à écrire devrait également être plus rapide: étant donné que la POO a l'intention que des éléments logiquement distincts soient représentés par différents objets, la vérification de l'égalité des références serait plus rapide, compte tenu du fait que les objets peuvent devenir assez volumineux.
D'un point de vue logique - l'égalité d'un objet à un autre ne doit pas être aussi évidente que la comparaison avec les propriétés de l'objet pour l'égalité (par exemple, comment null == null est-il interprété logiquement? Cela peut différer d'un cas à l'autre).
Je pense que cela se résume à votre observation selon laquelle "vous voulez toujours l'égalité logique sur l'égalité de référence". Le consensus parmi les concepteurs de langage était probablement le contraire. Personnellement, j'ai du mal à évaluer cela, car je n'ai pas le large éventail d'expérience en programmation. En gros, j'utilise davantage l'égalité de référence dans les algorithmes d'optimisation et l'égalité logique dans la gestion des ensembles de données.
la source
.equals()
compare les variables par leur contenu. au lieu de==
cela compare les objets par leur contenu ...l'utilisation d'objets est une utilisation plus précise de tu
.equals()
la source