Tout programmeur Java compétent sait que vous devez utiliser String.equals () pour comparer une chaîne, plutôt que == car == vérifie l’égalité des références.
Lorsque je traite avec des chaînes, je vérifie la plupart du temps l’égalité des valeurs plutôt que l’égalité des références. Il me semble que ce serait plus intuitif si le langage permettait la comparaison des valeurs de chaîne en utilisant simplement ==.
À titre de comparaison, l' opérateur == de C # vérifie l'égalité des valeurs pour la chaîne s. Et si vous avez réellement besoin de vérifier l’égalité des références, vous pouvez utiliser String.ReferenceEquals.
Un autre point important est que les chaînes sont immuables, il n'y a donc aucun mal à autoriser cette fonctionnalité.
Y a-t-il une raison particulière pour laquelle cela n'est pas implémenté en Java?
==
se trouvent l' égalité d'objet et l'eq
égalité de référence ( ofps.oreilly.com/titles/9780596155957/… ).==
opérateur ne correspond qu'àEquals
si l'==
opérateur a été implémenté de cette façon. Le comportement par défaut de==
est le même queReferenceEquals
(en fait,ReferenceEquals
est défini comme la version de l'objet de==
)Réponses:
Je suppose que ce n'est que cohérence, ou "principe du moindre étonnement". String est un objet, il serait donc surprenant que cet objet soit traité différemment des autres objets.
À l'époque de la sortie de Java (~ 1995), le simple
String
luxe était un luxe absolu pour la plupart des programmeurs habitués à représenter des chaînes comme des tableaux à terminaison nulle.String
Le comportement est maintenant ce qu'il était à l'époque, et c'est bien; changer subtilement le comportement par la suite pourrait avoir des effets surprenants et indésirables sur les programmes de travail.En guise de remarque, vous pouvez utiliser
String.intern()
une représentation canonique (internée) de la chaîne, après laquelle des comparaisons peuvent être effectuées==
. L'internat prend du temps, mais après cela, les comparaisons seront très rapides.Ajout: contrairement à ce que suggèrent certaines réponses, il ne s'agit pas de supporter la surcharge des opérateurs . L'
+
opérateur (concaténation) fonctionne surString
s même si Java ne prend pas en charge la surcharge d'opérateur; c'est simplement traité comme un cas spécial dans le compilateur, résolvant enStringBuilder.append()
. De même,==
aurait pu être traité comme un cas spécial.Alors pourquoi étonner avec cas particulier
+
mais pas avec==
? Parce que,+
tout simplement, ne compile pas lorsqu'il est appliqué à des non-String
objets, c'est donc rapidement apparent. Le comportement différent de==
serait beaucoup moins apparent et donc beaucoup plus étonnant quand il vous frappera.la source
James Gosling , le créateur de Java, l’a expliqué en juillet 2000 :
la source
==
opérateur est surchargé pour les objets et les primitives. L'+
opérateur est surchargé pourbyte
,short
,int
,long
,float
,double
,String
et probablement quelques autres j'ai oublié. Il aurait été parfaitement possible de surcharge==
pourString
aussi bien.Cohérence dans la langue. Avoir un opérateur qui agit différemment peut être surprenant pour le programmeur. Java n'autorise pas les utilisateurs à surcharger les opérateurs - par conséquent, l'égalité de référence est la seule signification raisonnable
==
entre les objets.Dans Java:
==
compare l'égalité numérique==
compare l'égalité booléenne==
compare l'identité de référence.equals(Object o)
pour comparer les valeursC'est ça. Règle simple et simple pour identifier ce que vous voulez. Tout cela est traité dans la section 15.21 du JLS . Il comprend trois sous-sections faciles à comprendre, à mettre en œuvre et à raisonner.
Une fois que vous autorisez la surcharge de
==
, le comportement exact n'est pas quelque chose que vous pouvez regarder vers le JLS et mettre le doigt sur un élément spécifique et dire "c'est comme ça que ça marche", le code peut devenir difficile à raisonner. Le comportement exact de==
peut être surprenant pour un utilisateur. Chaque fois que vous le voyez, vous devez revenir en arrière et vérifier ce que cela signifie réellement.Comme Java ne permet pas de surcharger les opérateurs, il est nécessaire de disposer d’un test d’égalité des valeurs que vous pouvez remplacer par la définition de base. Ainsi, il a été mandaté par ces choix de conception.
==
en Java, teste numeric pour les types numériques, égalité booléenne pour les types booléens et égalité de référence pour tout le reste (ce qui peut se substituer.equals(Object o)
à tout ce qu'ils veulent pour l'égalité des valeurs).Ce n’est pas une question de "existe-t-il un cas d’utilisation pour une conséquence particulière de cette décision de conception" mais plutôt "il s’agit d’une décision de conception visant à faciliter ces autres choses, c’est une conséquence de celle-ci".
String interning en est un exemple. Selon JLS 3.10.5 , tous les littéraux de chaîne sont internés. Les autres chaînes sont internées si on y invoque
.intern()
. Cela"foo" == "foo"
est vrai, est une conséquence des décisions de conception prises pour minimiser l’empreinte mémoire des littéraux de chaîne. Au-delà, l’internalisation des chaînes est quelque chose qui, au niveau de la JVM, a un peu de visibilité sur l’utilisateur, mais dans la très grande majorité des cas, ne devrait pas concerner le programmeur (et les cas d’utilisation pour quelque chose qui figurait en haut de la liste pour les concepteurs lorsqu’on envisage cette fonctionnalité).Les gens vont le souligner
+
et+=
sont surchargés pour String. Cependant, ce n'est ni ici ni là-bas. Cela reste le cas que si==
a une signification d'égalité de valeur pour String (et uniquement String), il faudrait une méthode différente (qui n'existe que dans String) pour l'égalité de référence. De plus, cela compliquerait inutilement les méthodes qui prennent Object et s’attendent==
à ce qu’elles se comportent d’une manière et.equals()
d’une autre, obligeant les utilisateurs à casse toutes ces méthodes pour String.Le contrat cohérent pour
==
on Objects est qu'il ne s'agit que d'égalité de référence et qu'il.equals(Object o)
existe pour tous les objets qui doivent tester l'égalité de valeur. Compliquer cela complique beaucoup trop de choses.la source
new String("foo") == new String("foo")
peut être vrai (voir Déduplication des chaînes ).Java ne prend pas en charge la surcharge des opérateurs, ce qui signifie
==
que cela ne s'applique qu'aux types primitifs ou aux références. Tout le reste nécessite l'invocation d'une méthode. Pourquoi les concepteurs ont-ils répondu à cette question? Si je devais deviner, c'est probablement parce que la surcharge des opérateurs ajoute une complexité qu'ils n'étaient pas intéressés à ajouter.Je ne suis pas un expert en C #, mais les concepteurs de ce langage semblent l'avoir mis en place de telle sorte que chaque primitive soit un
struct
et chaquestruct
est un objet. Etant donné que C # permet la surcharge des opérateurs, cette configuration permet à toute classe, et pas seulementString
, de se faire travailler de la manière "attendue" avec n'importe quel opérateur. C ++ permet la même chose.la source
==
signifiait l' égalité des cordes, il nous faudrait une autre notation pour l' égalité de référence.==
. Cela ajoute effectivement une surcharge d'opérateur, ce qui aurait des implications énormes sur la manière dont Java est implémenté.ClassName.ReferenceEquals(a,b)
), ainsi qu'un==
opérateur et uneEquals
méthode par défaut pointant tous les deuxReferenceEquals
.Cela a été fait différemment dans d'autres langues.
En Object Pascal (Delphi / Free Pascal) et C #, l'opérateur d'égalité est défini pour comparer des valeurs, et non des références, lors de l'utilisation de chaînes.
Particulièrement en Pascal, string est un type primitif (une des choses que j'aime beaucoup à propos de Pascal, obtenir NullreferenceException simplement à cause d'une chaîne non initialisée est tout simplement irritant) et avoir une sémantique de copie sur écriture rendant ainsi (la plupart du temps) très pas cher (en d’autres termes, n’est visible que lorsque vous commencez à concaténer des chaînes de plusieurs mégaoctets).
C'est donc une décision de conception de langage pour Java. Lorsqu'ils ont conçu le langage, ils ont suivi la méthode C ++ (comme Std :: String) pour que les chaînes soient des objets. IMHO est un hack permettant de compenser l'absence d'un type de chaîne réel dans C, au lieu de faire des chaînes une primitive (ce qu'elles sont).
Pour une raison donc, je ne peux que spéculer sur le fait qu’ils ont simplifié les choses au lieu de coder l’opérateur, ce qui a créé une exception pour les chaînes du compilateur.
la source
String
aurait dû être un type primitif en Java. Contrairement aux autres types, le compilateur doit connaîtreString
; de plus, les opérations sur ce sujet seront suffisamment courantes pour que, pour de nombreux types d’applications, elles puissent constituer un goulot d’étranglement en termes de performances (qui pourrait être atténué par le support natif). Un objetstring
[en minuscule] typique aurait un objet alloué sur le tas pour contenir son contenu, mais aucune référence "normale" à cet objet n'existerait nulle part; il pourrait donc s'agir d'un simple indirectChar[]
ouByte[]
plutôt que d'avoir à êtreChar[]
indirect par le biais d'un autre objet.En Java, il n’ya aucune surcharge d’opérateur, c’est pourquoi les opérateurs de comparaison ne sont surchargés que pour les types primitifs.
La classe 'String' n'est pas une primitive. Par conséquent, elle ne surcharge pas '==' et utilise par défaut la comparaison de l'adresse de l'objet dans la mémoire de l'ordinateur.
Je ne suis pas sûr, mais je pense que dans Java 7 ou 8 Oracle a fait une exception dans le compilateur de reconnaître
str1 == str2
commestr1.equals(str2)
la source
s1
ets2
leur donne le même contenu, elles transmettent las1.equals(s2)
comparaison d' égalité ( ) mais pas la comparaison de même référence (==
) car il s'agit de deux objets différents. Changer la sémantique de l'==
égalité signifie faires1 == s2
évaluertrue
où elle évaluaitfalse
.Java semble avoir été conçu pour respecter une règle fondamentale selon laquelle l'
==
opérateur doit être légal chaque fois qu'un opérateur peut être converti en un type, et doit comparer le résultat de cette conversion avec l'opérande non converti.Cette règle n’est pas propre à Java, mais elle a des effets de grande portée (et à mon humble avis malheureux) sur la conception d’autres aspects du langage liés au type. Il aurait été plus propre de préciser les comportements de
==
ce qui concerne les combinaisons particulières de types d'opérandes, et interdire les combinaisons de types X et Y oùx1==y1
etx2==y1
n'impliqueraientx1==x2
, mais les langues font rarement que [dans cette philosophie,double1 == long1
devrait soit indiquer sidouble1
n’est pas une représentation exacte delong1
, ou refuse de compiler;int1==Integer1
devrait être interdit, mais il devrait exister un moyen pratique et efficace de déterminer si un objet est un entier en boîte avec une valeur particulière (la comparaison avec quelque chose qui n'est pas un entier en boîte devrait simplement renvoyerfalse
)].En ce qui concerne l'application de l'
==
opérateur aux chaînes, si Java avait interdit les comparaisons directes entre des opérandes de typeString
et deObject
, il aurait pu éviter les surprises dans le comportement de==
, mais il n'y a aucun comportement qu'il puisse implémenter pour de telles comparaisons qui ne serait pas étonnant. Avoir deux références de chaîne conservées dans le typeObject
se comporte différemment des références conservées dans le typeString
aurait été beaucoup moins surprenant que le fait d'avoir l'un ou l'autre de ces comportements diffère de celui d'une comparaison légale de type mixte. Si celaString1==Object1
est légal, cela impliquerait que la seule façon pour que les comportementsString1==String2
et les comportementsObject1==Object2
correspondentString1==Object1
soit pour qu’ils se correspondent.la source
==
sur des objets devrait simplement appeler (Null-safe) des égaux et quelque chose d'autre (par exemple,===
ouSystem.identityEqual
) devrait être utilisé pour la comparaison d'identité. Le mélange des primitives et des objets serait initialement interdit (il n'y avait pas d'autoboxing avant la version 1.5), puis une règle simple pouvait être trouvée (par exemple, unbox null-safe, puis cast, puis compare).int==Integer
renvoyer un opérateurfalse
si leInteger
est, et sinon de comparer des valeurs, mais cette approche aurait été différente de celle de==
toutes les autres circonstances, où elle contraint inconditionnellement les deux opérandes au même type avant. en les comparant. Personnellement, je me demande si l'auto-unboxing a été mis en place dans le but de permettreint==Integer
à un comportement qui ne soit pasint
références et la comparaison des références auraient été ridicules [mais n'auraient pas toujours échoué]. Sinon, je ne vois aucune raison d'autoriser une conversion implicite pouvant échouer avec un NPE.==
n'a rien à voir avecidentityEquals
. +++ "Opérateurs d'égalité séparés pour l'égalité de valeur et de référence" - mais lesquels? Je considère à la fois primitive==
etequals
que faire la valeur de comparaison dans le sens où lesequals
regards à la valeur de la référence. +++ Quand cela est==
voulu direequals
, alorsint==Integer
DEVRAIT faire la sélection automatique et comparer les références en utilisant des équivalents null-safe. +++ J'ai bien peur que mon idée ne soit pas vraiment la mienne, mais ce que Kotlin fait.==
jamais l'égalité de référence n'a été testée, elle pourrait raisonnablement effectuer un test d'égalité de valeur null-safe. Le fait qu'il fait l' égalité de test de référence, cependant, limite sévèrement la façon dont il peut gérer des comparaisons référence / valeur mixtes sans contradiction. Notez également que Java est basé sur la notion que les opérateurs promeuvent les deux opérandes au même type, plutôt que de générer des comportements spéciaux basés sur les combinaisons de types impliqués. Par exemple,16777217==16777216.0f
retournetrue
parce qu'il effectue une conversion avec perte du premier opérande enfloat
...En général, il y a de très bonnes raisons de vouloir pouvoir tester si deux références d'objet pointent sur le même objet. J'ai eu beaucoup de fois que j'ai écrit
Je peux ou non avoir une fonction d'égal à égal dans de tels cas. Si je le fais, la fonction equals peut comparer tout le contenu des deux objets. Souvent, il ne fait que comparer un identifiant. "A et B sont des références au même objet" et "A et B sont deux objets différents ayant le même contenu" sont bien entendu deux idées très différentes.
Il est probablement vrai que pour les objets immuables, tels que Strings, le problème est moins grave. Avec des objets immuables, nous avons tendance à penser que l’objet et la valeur sont la même chose. Eh bien, quand je dis "nous", je veux dire "je", au moins.
Bien sûr, cela retourne faux, mais je peux voir quelqu'un penser que cela devrait être vrai.
Mais une fois que vous dites que == compare les poignées de référence et non le contenu des objets en général, il serait potentiellement déroutant de créer un cas particulier pour Strings. Comme quelqu'un d'autre l'a dit, si vous vouliez comparer les poignées de deux objets String? Y aurait-il une fonction spéciale pour le faire uniquement pour les chaînes?
Et à propos de ...
Est-ce faux parce que ce sont deux objets différents, ou vrai parce que ce sont des chaînes dont le contenu est égal?
Alors oui, je comprends à quel point les programmeurs sont désorientés. Je l'ai fait moi-même, je veux dire écrire si myString == "foo" quand je voulais dire si myString.equals ("foo"). Mais à moins de redéfinir la signification de l'opérateur == pour tous les objets, je ne vois pas comment y répondre.
la source
==
signifient "chaînes égales".==
, comme vous l’aviez mentionné à la fin.==
était sujette aux erreurs.Ceci est une question valable
Strings
, et non seulement pour les chaînes, mais aussi pour d' autres objets immuables représentant une « valeur », par exempleDouble
,BigInteger
et mêmeInetAddress
.Pour rendre l'
==
opérateur utilisable avec Strings et d'autres classes de valeur, je vois trois alternatives:Demandez au compilateur de connaître toutes ces classes de valeurs et la façon de comparer leurs contenus. Si cela ne représentait qu'une poignée de classes du
java.lang
paquet, je le prendrais en considération, mais cela ne couvre pas les cas comme InetAddress.Autoriser la surcharge de l'opérateur afin qu'une classe définisse son
==
comportement de comparaison.Supprimez les constructeurs publics et demandez aux méthodes statiques renvoyant des instances d'un pool, renvoyant toujours la même instance pour la même valeur. Pour éviter les fuites de mémoire, vous avez besoin de quelque chose comme SoftReferences dans le pool, qui n'existait pas dans Java 1.0. Et maintenant, pour maintenir la compatibilité, les
String()
constructeurs ne peuvent plus être supprimés.La seule chose qui pourrait encore être faite aujourd'hui serait d'introduire la surcharge d'opérateurs, et personnellement, je ne voudrais pas que Java aille dans cette voie.
Pour moi, la lisibilité du code est primordiale, et un programmeur Java sait que les opérateurs ont une signification fixe, définie dans la spécification du langage, alors que les méthodes sont définies par un code et que leur signification doit être recherchée dans la Javadoc de la méthode. Je voudrais rester avec cette distinction même si cela signifie que les comparaisons de chaînes ne pourront pas utiliser l'
==
opérateur.Il n’ya qu’un aspect des comparaisons de Java qui me dérange: l’effet de la boxe automatique et de la box-boxing. Il cache la distinction entre le type primitif et le type wrapper. Mais quand vous les comparez avec
==
, ils sont très différents.la source