Warum hat es der Vergleich der Operatorzeichenfolgenwerte nicht nach Java geschafft?

50

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?

l46kok
quelle
12
Vielleicht möchten Sie sich Scala ansehen, wo ==sich Objektgleichheit und eqReferenzgleichheit befinden ( ofps.oreilly.com/titles/9780596155957/… ).
Giorgio
Nur als Hinweis, und das hilft Ihnen vielleicht nicht weiter, aber soweit ich mich erinnere, können Sie String-Literale mit einem '=='
Kgrover
10
@Kgrover: Das können Sie, aber das ist nur ein praktisches Nebenprodukt der Referenzgleichheit und wie Java String-Matching-Literale aggressiv in Referenzen auf dasselbe Objekt optimiert. Mit anderen Worten, es funktioniert, aber aus den falschen Gründen.
Tdammers
1
@aviv Der ==Operator ordnet nur zu, Equalswenn der ==Operator auf diese Weise implementiert wurde. Das Standardverhalten für ==ist dasselbe wie ReferenceEquals(tatsächlich ReferenceEqualsist es definiert als die Objektversion von ==)
Patrick Huizinga
3
Dies ist eine Folge von Entwurfsentscheidungen, die in vielen anderen Szenarien sehr sinnvoll sind. Aber da Sie das zu wissen scheinen und dies trotzdem fragen, fühle ich mich gezwungen, Ihrer Frage entgegenzutreten: Warum muss es einen Anwendungsfall für den String-Referenzvergleich geben?
Jacob Raihle

Antworten:

90

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), Stringwar es für die meisten Programmierer, die es gewohnt waren, Strings als nullterminierte Arrays darzustellen, ein absoluter Luxus , nur so etwas zu haben . StringDas 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 auf Strings, obwohl Java das Überladen von Operatoren nicht unterstützt. Es wird einfach als Sonderfall im Compiler behandelt, in den es aufgelöst wird StringBuilder.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- StringObjekte 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.

Joonas Pulakka
quelle
8
Sonderfälle sorgen für Erstaunen.
Blrfl
17
Saiten waren 1995 ein Luxus? Ja wirklich?? Schauen Sie sich die Geschichte der Computersprachen an. Die Anzahl der Sprachen, die zu diesem Zeitpunkt eine Art Zeichenfolge hatten, würde die Anzahl der Sprachen, die dies nicht taten, bei weitem übertreffen. Wie viele Sprachen außer C und seinen Nachkommen verwendeten nullterminierte Arrays?
WarrenT
14
@WarrenT: Sicher, einige (wenn nicht die meisten) Sprachen hatten irgendeine Art von Zeichenfolge, aber Unicode-fähige, mit Speicherbereinigungen versehene Zeichenfolgen waren 1995 eine Neuheit, denke ich. Zum Beispiel führte Python Unicode-Strings mit der Version 2.0 aus dem Jahr 2000 ein. Die Wahl der Unveränderlichkeit war zu dieser Zeit ebenfalls umstritten.
Joonas Pulakka
3
@JoonasPulakka Dann sollten Sie vielleicht Ihre Antwort bearbeiten, um das zu sagen. Denn so wie es aussieht, ist der „totale Luxus“ -Teil Ihrer Antwort völlig falsch.
Svick
1
Internierung hat Kosten: Sie erhalten eine Zeichenfolge, die niemals freigegeben wird. (Nun, es sei denn, Sie verwenden Ihre eigene Internierungsmaschine, die Sie wegwerfen können.)
Donal Fellows
32

James Gosling , der Schöpfer von Java, hat es im Juli 2000 so erklärt :

Ich habe das Überladen von Operatoren als eine ziemlich persönliche Wahl ausgelassen, weil ich zu viele Leute gesehen habe, die es in C ++ missbraucht haben. Ich habe in den letzten fünf bis sechs Jahren viel Zeit damit verbracht, Leute über das Überladen von Betreibern zu befragen, und es ist wirklich faszinierend, weil man die Community in drei Teile zerlegt: Wahrscheinlich denken 20 bis 30 Prozent der Bevölkerung, dass Betreiber überladen werden Laich des Teufels; Jemand hat etwas mit Überladen von Operatoren gemacht, das sie nur wirklich angekreuzt hat, weil sie wie + zum Einfügen von Listen verwendet haben und das Leben wirklich verwirrend macht. Ein Großteil dieses Problems beruht auf der Tatsache, dass es nur etwa ein halbes Dutzend Operatoren gibt, die Sie sinnvoll überladen können, und dass es dennoch Tausende oder Millionen von Operatoren gibt, die die Leute definieren möchten - Sie müssen also auswählen,

Ross Patterson
quelle
50
Ah, ja, die alten "lassen Sie uns das spitze Werkzeug abstumpfen, damit sich die Dummköpfe nicht selbst verletzen", entschuldigen Sie.
Blrfl
22
@Blrfl: Wenn ein Werkzeug mehr Probleme verursacht als löst, ist es kein gutes Werkzeug. Die Entscheidung, ob dies bei einer Überlastung des Bedieners der Fall ist, könnte natürlich zu einer sehr langen Diskussion werden.
Giorgio
15
-1. Dies beantwortet die Frage überhaupt nicht. Java hat Operatorüberladung. Der ==Operator ist für Objekte und Grundelemente überladen. Der +Betreiber überlastet ist für byte, short, int, long, float, double, Stringund wahrscheinlich ein paar andere habe ich vergessen. Es wäre durchaus möglich, Überlastung gewesen ==für Stringauch.
Jörg W Mittag
10
@Jorg - nein das tut es nicht. Das Überladen von Bedienern kann auf Benutzerebene nicht definiert werden. Es gibt zwar einige Sonderfälle im Compiler, die sich jedoch kaum qualifizieren
AZ01
9
@Blrfl: Mir macht es nichts aus, wenn die Dummköpfe sich selbst verletzen. Wenn sie mir versehentlich die Augen ausstechen, ärgere ich mich.
Jonas
9

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
    • Verwenden Sie .equals(Object o)Werte vergleichen

Das 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
Vielen Dank, dass Sie sich die Zeit genommen haben, uns zu antworten. Dies wäre eine großartige Antwort auf die anderen Fragen, die ich verlinkt habe. Leider ist dies für diese Frage nicht geeignet. Ich werde das OP mit Erläuterungen auf der Grundlage der Kommentare aktualisieren. Ich suche eher nach Anwendungsfällen, bei denen ein Sprachbenutzer beim Vergleichen von Zeichenfolgen die falsch-negativen Ergebnisse haben möchte. Die Sprache bietet dieses Feature als Konsequenz, jetzt möchte ich, dass wir einen Schritt weiter gehen. Vielleicht denkt der neusprachige Designer darüber nach, ist das nötig? (leider kein lang-design.SE)
Anonsage
3
@Anonsage es ist kein falsches Negativ. Sie sind nicht dasselbe Objekt. Das ist alles was es sagt. Ich muss auch darauf hinweisen, dass in Java 8 new String("foo") == new String("foo")möglicherweise wahr ist (siehe String-Deduplizierung ).
1
Was das Sprachdesign angeht, wirbt CS.SE dafür, dass es dort möglicherweise ein Thema gibt.
Oh, danke! Ich werde dort meine zukünftigen Fragen zum Lang-Design posten. :) Und ja, leider ist 'falsch-negativ' nicht der genaueste Weg, um meine Frage und das, wonach ich suche, zu beschreiben. Ich muss mehr Wörter schreiben, damit die Leute nicht raten müssen, was ich bin versucht zu sagen.
Anonsage
2
"Konsistenz in der Sprache" hilft auch bei Generika
Brendan
2

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 structund jedes structein Objekt. Da C # das Überladen von Operatoren ermöglicht, ist es für jede Klasse sehr einfach String, sich mit jedem Operator auf die "erwartete" Weise arbeiten zu lassen. C ++ erlaubt dasselbe.

Blrfl
quelle
1
„Java unterstützt keine Überladen von Operatoren, die Mittel == gelten nur für primitive Typen oder Referenzen Alles , was erfordert anderer Aufruf einer Methode..“: Man könnte hinzufügen , dass , wenn ==Zeichenfolge Gleichheit gemeint, wir würden eine andere Schreibweise für Referenz Gleichheit müssen.
Giorgio
@ Giorgio: Genau. Siehe meinen Kommentar zu Gilad Naamans Antwort.
Blrfl
Dies kann jedoch durch eine statische Methode gelöst werden, die die Referenzen zweier Objekte (oder eines Operators) vergleicht. Wie zum Beispiel in C #.
Gilad Naaman
@ GiladNaaman: Das wäre ein Nullsummenspiel, weil es das Gegenteil von dem verursacht, was Java jetzt hat: Gleichheit wäre für einen Operator und Sie müssten eine Methode aufrufen, um Referenzen zu vergleichen. Außerdem müssten Sie festlegen, dass alle Klassen etwas implementieren, an das gebunden werden kann ==. Dies führt effektiv zu einer Überlastung der Operatoren, was enorme Auswirkungen auf die Implementierung von Java haben würde.
Blrfl
1
@Blrfl: Nicht wirklich. Es wird immer eine definierte Art und Weise zu Referenz vergleichen ( ClassName.ReferenceEquals(a,b)) und ein Standard - ==Operator und EqualsVerfahren sowohl zeigt auf ReferenceEquals.
Gilad Naaman
2

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.

Fabricio Araujo
quelle
Wie beantwortet dies die gestellte Frage?
Mücke
Siehe letzten Satz (den ich in einem richtigen Absatz in einer Bearbeitung getrennt habe).
Fabricio Araujo
1
Meiner Meinung nach Stringsollte ein primitiver Typ in Java sein. Im Gegensatz zu anderen Typen muss der Compiler wissen String; 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 typischer string[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 sein Char[]oder es muss Byte[]nicht Char[]durch ein anderes Objekt indirekt sein.
Supercat
1

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 == str2als zu erkennenstr1.equals(str2)

Gilad Naaman
quelle
"Ich bin mir nicht sicher, aber ich denke, dass Oracle in Java 7 oder 8 eine Ausnahme im Compiler gemacht hat, um str1 == str2 als str1.equals (str2) zu erkennen": Ich wäre nicht überrascht: Oracle scheint weniger besorgt zu sein mit Minimalismus als Sun.
Giorgio
2
Wenn das stimmt, ist das ein sehr hässlicher Hack, da es jetzt eine Klasse gibt, die die Sprache anders als alle anderen behandelt und Code bricht, der Referenzen vergleicht. : - @
Blrfl
1
@ WillihamTotland: Betrachten Sie den umgekehrten Fall. Derzeit , wenn ich zwei Strings zu erstellen, s1und s2und 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 veranlassen s1 == s2, auszuwerten, truewo es verwendet wurde, um auszuwerten false.
Blrfl
2
@Brlfl: Das stimmt zwar, aber das klingt nach einer außergewöhnlich schlechten Sache, auf die man sich verlassen kann, da Strings unveränderliche, internierbare Objekte sind.
Williham Totland
2
@Giorgio: „Java 7 oder 8 Orakel eine Ausnahme gemacht , in dem Compiler str1 zu erkennen == str2 als str1.equals (STR2)“ Nein, das Java Language Specification sagt: Referenz Gleichheitsoperatoren : „Wenn die Operanden eines Gleichheitsoperator sind Sowohl vom Verweistyp als auch vom NULL-Typ, dann ist die Operation Objektgleichheit. " Das war's Leute. Darüber habe ich auch im Java 8-Frühentwurf nichts Neues gefunden .
David Tonhofer
0

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, wo x1==y1und x2==y1würde dies nicht implizieren x1==x2, aber Sprachen, die dies selten tun, double1 == long1müssten entweder angeben, ob double1ist keine exakte Darstellung von long1oder lehnt die Kompilierung ab;int1==Integer1sollte 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ückgeben false).

In Bezug auf das Anwenden des ==Operators auf Zeichenfolgen hätte Java, wenn direkte Vergleiche zwischen Operanden des Typs Stringund verboten worden wären Object, Ü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, Objectsich anders verhalten als Verweise, die in Typ gehalten werden String, als wenn sich eines dieser Verhalten von dem eines legalen Vergleichs gemischter Typen unterscheidet. Wenn dies String1==Object1legal ist, würde dies bedeuten, dass die Verhaltensweisen von String1==String2und Object1==Object2nur übereinstimmen String1==Object1, wenn sie übereinstimmen.

Superkatze
quelle
Ich muss etwas vermissen, aber IMHO ==für Objekte sollte einfach (null-safe) gleich sein und etwas anderes (zB ===oder System.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).
Maaartinus
@maaartinus: Ein gutes Sprachdesign sollte getrennte Gleichheitsoperatoren für Wert- und Referenzgleichheit verwenden. Ich bin damit einverstanden, dass es konzeptionell möglich gewesen wäre, einen int==IntegerOperator zurückzugeben, falsewenn der Integernull 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, um int==Integerein Verhalten zuzulassen , das nicht unsinnig war ...
Supercat
... da das Autoboxen intund 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.
Supercat
Ich denke, dass meine Idee konsequent ist. Denken Sie nur daran, dass in der besseren Welt ==nichts damit zu tun hat identityEquals. +++ "Gleichheitsoperatoren für Wert- und Referenzgleichheit trennen" - aber welche? Ich würde sowohl primitiv ==als auch equalsals Wertvergleich in dem Sinne equalsbetrachten, in dem der Wert der Referenz betrachtet wird. +++ Wenn dies ==beabsichtigt ist equals, int==IntegerSOLLTEN 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.
Maaartinus
@maaartinus: Wenn ==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.0ftruefloat
Gibt
0

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

Address oldAddress;
Address newAddress;
... populate values ...
if (oldAddress==newAddress)
... etc ...

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".

Integer three=new Integer(3);
Integer triangle=new Integer(3);
if (three==triangle) ...

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 ...

Object x=new String("foo");
Object y=new String("foo");
if (x==y) ...

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.

Jay
quelle
Beachten Sie, dass andere moderne Sprachen in der JVM, wie z. B. Scala, =="gleiche Zeichenfolgen" bedeuten.
Andres F.
@AndresF. (Achselzucken) In Java bedeutet "<" "kleiner als", in XML "öffnet es ein Tag". In VB kann "=" bedeuten, dass "gleich" ist, während es in Java nur zur Zuweisung verwendet wird. Es ist nicht verwunderlich, dass verschiedene Sprachen verschiedene Symbole verwenden, um dasselbe zu bedeuten, oder dasselbe Symbol, um verschiedene Dinge zu bedeuten.
Jay
Es war kein Grab an deiner Antwort. Es war nur ein Kommentar. Ich habe nur darauf hingewiesen, dass eine modernere Sprache als Java die Chance ergriffen hat, die Bedeutung von neu zu definieren ==, wie Sie am Ende bereits erwähnt haben.
Andres F.
@AndresF. Und meine Antwort war kein Anstoß für Ihren Kommentar, nur zu sagen, dass verschiedene Sprachen diese Probleme auf unterschiedliche Weise angehen. :-) Mir gefällt eigentlich die Art und Weise, wie VB damit umgeht ... Pause für das Zischen und Stöhnen der VB-Hasser ... "=" vergleicht immer den Wert (für systemdefinierte Typen), egal ob primitiv oder objektorientiert. "Is" vergleicht die Handles zweier Objekte. Das erscheint mir intuitiver.
Jay
Sicher. Aber Scala ist Java viel näher als Visual Basic. Ich denke gerne, dass Scalas Designer erkannt haben, dass Javas Verwendung ==fehleranfällig war.
Andres F.
0

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 Beispiel Double, BigIntegerund sogar InetAddress.

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.langPaket 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.

    int i=123456;
    Integer j=123456;
    Integer k=123456;
    System.out.println(i==j);  // true or false? Do you know without reading the specs?
    System.out.println(j==k);  // true or false? Do you know without reading the specs?
Ralf Kleberhoff
quelle