Jeder kompetente Java-Programmierer weiß, dass Sie String.equals () verwenden müssen, um einen String zu vergleichen, anstatt == weil == auf Referenzgleichheit prüft.
Wenn ich mit Strings zu tun habe, überprüfe ich die meiste Zeit auf Wertgleichheit und nicht auf Referenzgleichheit. Es scheint mir, dass es intuitiver wäre, wenn die Sprache den Vergleich von Zeichenfolgenwerten nur mit == zulässt.
Zum Vergleich prüft der Operator == von C # die Wertgleichheit für die Zeichenfolge s. Und wenn Sie wirklich nach Referenzgleichheit suchen mussten, können Sie String.ReferenceEquals verwenden.
Ein weiterer wichtiger Punkt ist, dass Strings unveränderlich sind, sodass diese Funktion keinen Schaden anrichtet.
Gibt es einen bestimmten Grund, warum dies nicht in Java implementiert ist?
quelle
==
sich Objektgleichheit undeq
Referenzgleichheit befinden ( ofps.oreilly.com/titles/9780596155957/… ).==
Operator ordnet nur zu,Equals
wenn der==
Operator auf diese Weise implementiert wurde. Das Standardverhalten für==
ist dasselbe wieReferenceEquals
(tatsächlichReferenceEquals
ist es definiert als die Objektversion von==
)Antworten:
Ich denke, es ist nur Beständigkeit oder "Grundsatz des geringsten Erstaunens". String ist ein Objekt, daher wäre es überraschend, wenn es anders behandelt würde als andere Objekte.
Zu der Zeit, als Java herauskam (~ 1995),
String
war es für die meisten Programmierer, die es gewohnt waren, Strings als nullterminierte Arrays darzustellen, ein absoluter Luxus , nur so etwas zu haben .String
Das Verhalten ist jetzt das, was es damals war, und das ist gut so. Eine nachträgliche subtile Änderung des Verhaltens könnte überraschende, unerwünschte Auswirkungen auf die Arbeitsprogramme haben.Als Randnotiz könnten Sie verwenden
String.intern()
, um eine kanonische (internierte) Darstellung der Zeichenfolge zu erhalten, nach der Vergleiche mit vorgenommen werden könnten==
. Das Internieren dauert einige Zeit, aber danach sind die Vergleiche sehr schnell.Zusatz: Im Gegensatz zu einigen anderen Antworten geht es nicht darum, das Überladen von Operatoren zu unterstützen . Der
+
Operator (Verkettung) funktioniert aufString
s, obwohl Java das Überladen von Operatoren nicht unterstützt. Es wird einfach als Sonderfall im Compiler behandelt, in den es aufgelöst wirdStringBuilder.append()
. Ebenso==
hätte als Sonderfall gehandelt werden können.Warum dann mit Sonderfall überraschen,
+
aber nicht mit==
? Weil es+
einfach nicht kompiliert, wenn es auf Nicht-String
Objekte angewendet wird, sodass dies schnell ersichtlich wird. Das unterschiedliche Verhalten von==
wäre viel weniger auffällig und damit viel erstaunlicher, wenn es dich trifft.quelle
James Gosling , der Schöpfer von Java, hat es im Juli 2000 so erklärt :
quelle
==
Operator ist für Objekte und Grundelemente überladen. Der+
Betreiber überlastet ist fürbyte
,short
,int
,long
,float
,double
,String
und wahrscheinlich ein paar andere habe ich vergessen. Es wäre durchaus möglich, Überlastung gewesen==
fürString
auch.Konsistenz innerhalb der Sprache. Einen Operator zu haben, der sich anders verhält, kann für den Programmierer überraschend sein. Java erlaubt Benutzern nicht, Operatoren zu überladen. Daher ist die Referenzgleichheit die einzig sinnvolle Bedeutung für
==
Objekte.Innerhalb von Java:
==
Vergleicht zwischen numerischen Typen die numerische Gleichheit==
Vergleicht die boolesche Gleichheit zwischen booleschen Typen==
Vergleicht die Referenzidentität zwischen Objekten.equals(Object o)
Werte vergleichenDas ist es. Einfache Regel und einfach zu identifizieren, was Sie wollen. Dies alles wird in Abschnitt 15.21 des JLS behandelt . Es enthält drei Unterabschnitte, die leicht zu verstehen, zu implementieren und zu verstehen sind.
Sobald Sie das Überladen von zulassen
==
, können Sie nicht mehr auf die JLS schauen und mit dem Finger auf ein bestimmtes Element zeigen und sagen, "so funktioniert es". Der Code ist möglicherweise schwer zu überlegen. Das genaue Verhalten von==
kann für einen Benutzer überraschend sein. Jedes Mal, wenn Sie es sehen, müssen Sie zurückgehen und überprüfen, was es tatsächlich bedeutet.Da Java keine Überladung von Operatoren zulässt, muss ein Wertegleichheitstest möglich sein, mit dem Sie die Basisdefinition von überschreiben können. Daher wurde es von diesen Entwurfsentscheidungen beauftragt.
==
In Java werden numerische Tests für numerische Typen, boolesche Gleichheit für boolesche Typen und Referenzgleichheit für alle anderen Tests (die überschreiben können.equals(Object o)
, was immer sie für die Wertgleichheit wollen) durchgeführt.Hierbei handelt es sich nicht um "Gibt es einen Anwendungsfall für eine bestimmte Konsequenz dieser Entwurfsentscheidung", sondern um "Dies ist eine Entwurfsentscheidung, um diese anderen Dinge zu erleichtern. Dies ist eine Konsequenz daraus."
String-Internierung ist ein Beispiel dafür. Gemäß JLS 3.10.5 werden alle String-Literale interniert. Andere Zeichenketten werden interniert, wenn man sie aufruft
.intern()
. Dies"foo" == "foo"
ist eine Konsequenz von Entwurfsentscheidungen, die getroffen wurden, um den von String-Literalen beanspruchten Speicherbedarf zu minimieren. Darüber hinaus ist das Internieren von Zeichenfolgen etwas auf der JVM-Ebene, das für den Benutzer ein wenig exponiert ist. In den allermeisten Fällen sollte es jedoch nicht den Programmierer betreffen (und Anwendungsfälle für Programmierer nicht) Dies stand für die Designer ganz oben auf der Liste, als sie diese Funktion in Betracht zogen.Die Leute werden darauf hinweisen
+
und+=
sind für String überladen. Das ist jedoch weder hier noch da. Es bleibt der Fall,==
dass eine andere Methode (die nur in String vorhanden ist) für die Referenzgleichheit benötigt wird, wenn der Wert für String (und nur für String) die Bedeutung Gleichheit hat. Darüber hinaus würde dies unnötigerweise Methoden verkomplizieren, die Object benötigen und erwarten==
, dass sie sich in die eine und.equals()
in die andere Richtung verhalten, wobei die Benutzer alle diese Methoden für String in Sonderfällen ausführen müssen.Der konsistente Vertrag für
==
on Objects ist, dass es sich nur um Referenzgleichheit handelt und dass er.equals(Object o)
für alle Objekte existiert, die auf Wertgleichheit getestet werden sollen . Dies zu komplizieren, kompliziert viel zu viele Dinge.quelle
new String("foo") == new String("foo")
möglicherweise wahr ist (siehe String-Deduplizierung ).Java unterstützt das Überladen von Operatoren nicht, was bedeutet, dass es
==
nur für primitive Typen oder Referenzen gilt. Alles andere erfordert den Aufruf einer Methode. Warum die Designer dies taten, ist eine Frage, die nur sie beantworten können. Wenn ich raten musste, liegt es wahrscheinlich daran, dass das Überladen von Operatoren Komplexität mit sich bringt, an deren Hinzufügen sie nicht interessiert waren.Ich bin kein Experte für C #, aber die Designer dieser Sprache scheinen es so eingerichtet zu haben, dass jedes Primitiv ein Objekt ist
struct
und jedesstruct
ein Objekt. Da C # das Überladen von Operatoren ermöglicht, ist es für jede Klasse sehr einfachString
, sich mit jedem Operator auf die "erwartete" Weise arbeiten zu lassen. C ++ erlaubt dasselbe.quelle
==
Zeichenfolge Gleichheit gemeint, wir würden eine andere Schreibweise für Referenz Gleichheit müssen.==
. Dies führt effektiv zu einer Überlastung der Operatoren, was enorme Auswirkungen auf die Implementierung von Java haben würde.ClassName.ReferenceEquals(a,b)
) und ein Standard -==
Operator undEquals
Verfahren sowohl zeigt aufReferenceEquals
.Dies wurde in anderen Sprachen anders gemacht.
In Object Pascal (Delphi / Free Pascal) und C # ist der Gleichheitsoperator definiert, um Werte und keine Referenzen zu vergleichen, wenn Zeichenfolgen verarbeitet werden.
Insbesondere in Pascal ist string ein primitiver Typ (eines der Dinge, die ich an Pascal sehr liebe, weil es einfach irritierend ist, NullreferenceException nur wegen eines nicht initialisierten Strings zu erhalten) und hat eine Schreib-Kopier-Semantik, die (die meiste Zeit) String-Operationen sehr macht billig (mit anderen Worten, erst bemerkbar, wenn Sie anfangen, Multi-Megabyte-Strings zu verketten).
Es ist also eine Entscheidung für das Sprachdesign in Java. Als sie die Sprache entwarfen, folgten sie dem C ++ - Weg (wie Std :: String), so dass Strings Objekte sind, was meiner Meinung nach ein Hack ist, um C zu kompensieren, dem ein realer String-Typ fehlt, anstatt Strings zu einem Primitiven zu machen (was sie sind).
Aus einem bestimmten Grund kann ich nur spekulieren, dass sie das so einfach gemacht haben und nicht, dass der Operator eine Ausnahme für den Compiler für Strings darstellt.
quelle
String
sollte ein primitiver Typ in Java sein. Im Gegensatz zu anderen Typen muss der Compiler wissenString
; Darüber hinaus sind Vorgänge in diesem Bereich hinreichend häufig, sodass sie für viele Arten von Anwendungen einen Leistungsengpass darstellen können (der durch systemeigenen Support gelindert werden könnte). Ein typischerstring
[Kleinbuchstabe] würde ein Objekt auf dem Heap haben, um dessen Inhalt zu speichern, aber nirgendwo würde ein "normaler" Verweis auf dieses Objekt existieren. Es könnte also ein einfach indirektes Objekt seinChar[]
oder es mussByte[]
nichtChar[]
durch ein anderes Objekt indirekt sein.In Java gibt es keinerlei Überladung von Operatoren, weshalb die Vergleichsoperatoren nur für die primitiven Typen überladen sind.
Die 'String'-Klasse ist kein Grundelement, hat also keine Überladung für' == 'und verwendet die Standardeinstellung zum Vergleichen der Adresse des Objekts im Arbeitsspeicher des Computers.
Ich bin mir nicht sicher, aber ich denke, dass in Java 7 oder 8 Orakel eine Ausnahme im Compiler gemacht hat,
str1 == str2
als zu erkennenstr1.equals(str2)
quelle
s1
unds2
und geben ihnen den gleichen Inhalt, übergeben sie die Gleichheit (s1.equals(s2)
) Vergleich aber nicht das gleiche Verweis (==
) Vergleich , weil sie zwei verschiedene Objekte. Das Ändern der Semantik von==
zu bedeuten Gleichheit würde veranlassens1 == s2
, auszuwerten,true
wo es verwendet wurde, um auszuwertenfalse
.Java wurde anscheinend entwickelt, um eine grundlegende Regel einzuhalten, wonach der
==
Operator immer dann legal sein sollte, wenn ein Operand in den Typ des anderen konvertiert werden kann, und das Ergebnis einer solchen Konvertierung mit dem nicht konvertierten Operanden zu vergleichen.Diese Regel ist für Java kaum einzigartig, hat jedoch einige weitreichende (und meiner Meinung nach bedauerliche) Auswirkungen auf die Gestaltung anderer typbezogener Aspekte der Sprache. Es wäre sauberer gewesen, das Verhalten in
==
Bezug auf bestimmte Kombinationen von Operandentypen zu spezifizieren und Kombinationen der Typen X und Y zu verbieten, wox1==y1
undx2==y1
würde dies nicht implizierenx1==x2
, aber Sprachen, die dies selten tun,double1 == long1
müssten entweder angeben, obdouble1
ist keine exakte Darstellung vonlong1
oder lehnt die Kompilierung ab;int1==Integer1
sollte verboten sein, aber es sollte ein praktisches und effizientes Nicht-Auslösemittel geben, um zu testen, ob ein Objekt eine Ganzzahl mit einem bestimmten Wert ist (ein Vergleich mit etwas, das keine Ganzzahl mit einem Kasten ist, sollte einfach zurückgebenfalse
).In Bezug auf das Anwenden des
==
Operators auf Zeichenfolgen hätte Java, wenn direkte Vergleiche zwischen Operanden des TypsString
und verboten worden wärenObject
, Überraschungen im Verhalten von ziemlich gut vermieden==
, aber es gibt kein Verhalten, das es für solche Vergleiche implementieren könnte, was nicht erstaunlich wäre. Es wäre weitaus weniger verwunderlich gewesen, wenn zwei Zeichenfolgenverweise, die in Typ gehalten werden,Object
sich anders verhalten als Verweise, die in Typ gehalten werdenString
, als wenn sich eines dieser Verhalten von dem eines legalen Vergleichs gemischter Typen unterscheidet. Wenn diesString1==Object1
legal ist, würde dies bedeuten, dass die Verhaltensweisen vonString1==String2
undObject1==Object2
nur übereinstimmenString1==Object1
, wenn sie übereinstimmen.quelle
==
für Objekte sollte einfach (null-safe) gleich sein und etwas anderes (zB===
oderSystem.identityEqual
) sollte für den Identitätsvergleich verwendet werden. Das Mischen von Primitiven und Objekten ist anfangs verboten (vor 1.5 gab es kein Autoboxing) und dann wurden einige einfache Regeln gefunden (z. B. nullsicheres Unboxen, dann Casten, dann Vergleichen).int==Integer
Operator zurückzugeben,false
wenn derInteger
null ist, und ansonsten Werte zu vergleichen, wäre dieser Ansatz anders gewesen als das Verhalten unter==
allen anderen Umständen, bei denen beide Operanden bedingungslos zu demselben Typ zuvor gezwungen wurden vergleicht sie. Persönlich frage ich mich, ob Auto-Unboxing eingerichtet wurde, umint==Integer
ein Verhalten zuzulassen , das nicht unsinnig war ...int
und das Durchführen eines Referenzvergleichs albern gewesen wäre [würde aber nicht immer scheitern]. Ansonsten sehe ich keinen Grund, eine implizite Konvertierung zuzulassen, die mit einer NPE fehlschlagen kann.==
nichts damit zu tun hatidentityEquals
. +++ "Gleichheitsoperatoren für Wert- und Referenzgleichheit trennen" - aber welche? Ich würde sowohl primitiv==
als auchequals
als Wertvergleich in dem Sinneequals
betrachten, in dem der Wert der Referenz betrachtet wird. +++ Wenn dies==
beabsichtigt istequals
,int==Integer
SOLLTEN Sie Autoboxen erstellen und die Referenzen mit nullsicheren Gleichungen vergleichen. +++ Ich fürchte, meine Idee ist nicht wirklich meine, sondern genau das, was Kotlin tut.==
die Referenzgleichheit nie getestet wurde, kann ein nullsicherer Wertgleichheitstest sinnvoll durchgeführt werden. Die Tatsache, dass die Referenzgleichheit getestet wird, schränkt jedoch den Umgang mit gemischten Referenz- / Wertevergleichen ohne Inkonsistenz erheblich ein. Beachten Sie auch, dass Java auf die Vorstellung festgelegt ist, dass Operatoren beide Operanden auf den gleichen Typ heraufstufen, anstatt basierend auf den Kombinationen der beteiligten Typen ein spezielles Verhalten zu erzielen.16777217==16777216.0f
true
float
Im Allgemeinen gibt es sehr gute Gründe, testen zu wollen, ob zwei Objektreferenzen auf dasselbe Objekt verweisen. Ich hatte viele Male, die ich geschrieben habe
In solchen Fällen kann ich eine gleiche Funktion haben oder auch nicht. In diesem Fall vergleicht die Funktion equals möglicherweise den gesamten Inhalt beider Objekte. Oft vergleicht es nur einen Bezeichner. "A und B sind Verweise auf dasselbe Objekt" und "A und B sind zwei verschiedene Objekte mit demselben Inhalt" sind natürlich zwei sehr unterschiedliche Ideen.
Es ist wahrscheinlich richtig, dass dies für unveränderliche Objekte wie Strings weniger ein Problem darstellt. Bei unveränderlichen Objekten neigen wir dazu, das Objekt und den Wert als dasselbe zu betrachten. Nun, wenn ich "wir" sage, meine ich zumindest "ich".
Das ist natürlich falsch, aber ich kann sehen, dass jemand denkt, dass es wahr sein sollte.
Wenn Sie jedoch sagen, dass == Referenzhandles und nicht den Inhalt von Objekten im Allgemeinen vergleicht, wäre es möglicherweise verwirrend, einen Sonderfall für Strings zu erstellen. Wie jemand anderes hier sagte, was ist, wenn Sie die Handles zweier String-Objekte vergleichen möchten? Wäre es eine spezielle Funktion, die es nur für Strings gibt?
Und was ist mit ...
Ist das falsch, weil es sich um zwei verschiedene Objekte handelt, oder wahr, weil es sich um Strings handelt, deren Inhalt gleich ist?
Also ja, ich verstehe, wie Programmierer dadurch verwirrt werden. Ich habe es selbst gemacht, ich meine schreiben, wenn meinString == "foo", wenn ich meinte, wenn meinString.equals ("foo"). Aber ohne die Bedeutung des Operators == für alle Objekte umzugestalten, verstehe ich nicht, wie man das anspricht.
quelle
==
"gleiche Zeichenfolgen" bedeuten.==
, wie Sie am Ende bereits erwähnt haben.==
fehleranfällig war.Dies ist eine gültige Frage
Strings
, und zwar nicht nur für Streicher, sondern auch für andere unveränderliche Objekte repräsentieren einigen „Wert“, zum BeispielDouble
,BigInteger
und sogarInetAddress
.Um den
==
Operator mit Strings und anderen Wertklassen verwendbar zu machen, sehe ich drei Alternativen:Informieren Sie den Compiler über alle diese Wertklassen und die Art und Weise, wie deren Inhalte verglichen werden können. Wenn es nur eine Handvoll Klassen aus dem
java.lang
Paket wäre, würde ich das in Betracht ziehen, aber Fälle wie InetAddress werden davon nicht abgedeckt.Ermöglichen Sie das Überladen von Operatoren, damit eine Klasse ihr
==
Vergleichsverhalten definiert .Entfernen Sie die öffentlichen Konstruktoren und lassen Sie statische Methoden Instanzen aus einem Pool zurückgeben, wobei immer dieselbe Instanz für denselben Wert zurückgegeben wird. Um Speicherverluste zu vermeiden, benötigen Sie SoftReferences im Pool, die in Java 1.0 nicht vorhanden waren. Aus Gründen der Kompatibilität können die
String()
Konstruktoren jetzt nicht mehr entfernt werden.Das Einzige, was heute noch getan werden könnte, wäre das Überladen von Operatoren, und ich persönlich möchte nicht, dass Java diesen Weg einschlägt.
Für mich ist die Lesbarkeit des Codes am wichtigsten, und ein Java-Programmierer weiß, dass die Operatoren eine feste Bedeutung haben, die in der Sprachspezifikation definiert ist, wohingegen Methoden durch Code definiert sind und ihre Bedeutung im Javadoc der Methode nachgeschlagen werden muss. Ich möchte bei dieser Unterscheidung bleiben, auch wenn dies bedeutet, dass Zeichenfolgenvergleiche den
==
Operator nicht verwenden können.Es gibt nur einen Aspekt von Javas Vergleichen, der mich nervt: den Effekt von Auto-Boxing und -unboxing. Es verbirgt die Unterscheidung zwischen dem primitiven und dem Wrapper-Typ. Aber wenn Sie sie mit vergleichen
==
, sind sie sehr unterschiedlich.quelle