Ausführung des Java-Zuweisungsoperators

76

In Java verstehe ich, dass die Zuweisung den Wert des richtigen Operanden ergibt, also Anweisungen wie x == (y = x)evaluieren true.

Dieser Code wird jedoch ausgegeben false.

public static void main(String[]args){
    String x = "hello";
    String y = "goodbye";
    System.out.println(x.equals(x = y));
}

Warum ist das? Nach meinem Verständnis wird zuerst ausgewertet (x = y), der xden Wert von zuweist y, und dann der Wert von zurückgegeben y. Dann x.equals(y)wird ausgewertet, was trueda sein sollte xund yjetzt die gleichen Referenzen teilen soll, aber stattdessen bekomme ich false.

Screenshot mit der Quelle und der Ausgabe "false"

Was passiert hier?

Sam
quelle
13
Ich denke, Sie wollten Ergebnis fürx.equals( y = x )
nits.kk
1
Könnte der Compiler inline xund y?
Lino
3
Gehen Sie davon aus, dass die Zuweisung x = yauf der rechten Seite ausgeführt wird , bevor die xauf der linken Seite bewertet wird?
Khelwood
@khelwood ja, das war meine Annahme. Es darf nicht
Sam
1
@ nits.kk Das glaube ich nicht. OP hat bereits gesagt, dass sie verstehen, dass dies x == (y = x)als wahr bewertet wird. Das Verhalten dessen, was Sie vorschlagen, wäre dann offensichtlich ...
Pedro A

Antworten:

76

Zuallererst: Das ist eine interessante Frage, sollte aber niemals in "echtem Code" auftauchen, da die Zuweisung zu der Variablen, die Sie in derselben Zeile aufrufen, verwirrend ist, selbst wenn Sie wissen, wie sie funktioniert.

Was hier passiert, sind diese 3 Schritte:

  1. Finden Sie heraus, für welches Objekt die Methode aufgerufen werden soll (dh bewerten Sie das erste x, dies führt zu einem Verweis auf die Zeichenfolge "Hallo").
  2. Finden Sie die Parameter heraus (dh bewerten Sie x = y, was sich ändert x, um auf die Zeichenfolge "Auf Wiedersehen" zu zeigen und auch einen Verweis auf diese Zeichenfolge zurückzugeben).
  3. Rufen Sie die Methode equalsfür das Ergebnis von # 1 auf und verwenden Sie das Ergebnis von # 2 als Parameter (der auf die Zeichenfolgen "Hallo" bzw. "Auf Wiedersehen" verweist).

Ein Blick auf den für diese Methode erstellten Bytecode macht deutlich (vorausgesetzt, Sie sprechen fließend Java-Bytecode):

     0: ldc           #2                  // String hello
     2: astore_1
     3: ldc           #3                  // String goodbye
     5: astore_2
     6: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
     9: aload_1
    10: aload_2
    11: dup
    12: astore_1
    13: invokevirtual #5                  // Method java/lang/String.equals:(Ljava/lang/Object;)Z
    16: invokevirtual #6                  // Method java/io/PrintStream.println:(Z)V
    19: return

Zeile 9 ist Schritt 1 oben (dh wertet xden Wert aus und merkt sich ihn).

Zeile 10-12 ist Schritt 2. Sie wird geladen y, dupliziert (einmal zum Zuweisen, einmal für den Rückgabewert des Zuweisungsausdrucks) und zugewiesen x.

Zeile 13 ruft equalsdas in Zeile 9 berechnete Ergebnis und das Ergebnis der Zeilen 10-12 auf.

Joachim Sauer
quelle
36
TL; DR: x.equals(x = y)=> "hello".equals(x = y)=> "hello".equals(x = "goodbye")=> "hello".equals("goodbye")=> false.
Bernhard Barker
8
Ein wichtiger Punkt ist, dass dies .eine höhere Priorität hat als= .
Gaurang Tandon
4
Hier geht es mehr um die Bewertungsreihenfolge als um die Priorität. Die Klammern machen den Vorrang sowieso irrelevant.
Amalloy
38

Gute Frage! Und die JLS hat die Antwort ...

§15.12.4.1 (Beispiel 15.12.4.1-2). Auswertungsreihenfolge während des Methodenaufrufs:

Im Rahmen eines Aufrufs einer Instanzmethode gibt es einen Ausdruck, der das aufzurufende Objekt angibt. Dieser Ausdruck scheint vollständig ausgewertet zu sein, bevor ein Teil eines Argumentausdrucks für den Methodenaufruf ausgewertet wird.

Also, in:

String x = "hello";
String y = "goodbye";
System.out.println(x.equals(x = y));

Das Auftreten von xbefore .equalswird zuerst vor dem Argumentausdruck ausgewertet x = y.

Daher wird ein Verweis auf die Zeichenfolge   hello  als Zielreferenz gespeichert, bevor die lokale Variable xgeändert wird, um auf die Zeichenfolge zu verweisen goodbye. Infolgedessen wird die equalsMethode für das Zielobjekt hellomit Argument aufgerufen goodbye, sodass das Ergebnis des Aufrufs lautet false.

Oleksandr Pyrohov
quelle
28

Es ist wichtig, sich daran zu erinnern, dass ein Stringin Java ein Objekt und daher eine Referenz ist. Wenn du anrufst

x.equals(...)

Es wird überprüft , ob der Wert an der Stelle verwiesen werden derzeit von xgleich , was Sie vorbei in. Im Innern Sie den Wert ändern, der xwird Referenzierung , aber Sie sind immer noch telefonieren equalsmit der ursprünglichen Referenz (der Verweis auf „Hallo“). Im Moment wird Ihr Code also verglichen, um festzustellen, ob "Hallo" gleich "Auf Wiedersehen" ist, was eindeutig nicht der Fall ist. Wenn Sie nach diesem Punkt xerneut verwenden, wird auf denselben Wert wie y verwiesen.

Keveloper
quelle
Wenn ich dich verstehe, sollte dies: "hello".equals((x = y))zurückkehren true?
Halayem Anis
3
Nein, denn (x = y) gibt den Wert von y zurück, was "Auf Wiedersehen" ist. Wenn Sie sich also "verabschieden" .equals (x = y), würde dies true zurückgeben
Keveloper
5

x=yin der Klammer bedeutet, dass der Ausdruck (x=y)jetzt ist goodbye, während das äußere x in x.equalsden Wert enthälthello

Chetan Jadhav CD
quelle
2
Dies erklärt wirklich nicht, warum dies geschieht, und liefert auch keine zusätzlichen Details, die für andere hilfreich wären.
Grau
Jetzt, wo ich es laut vorlese, stimme ich Ihnen eher zu. Die anderen Antworten sind ziemlich ausführlich, um sie nicht zu bearbeiten.
Chetan Jadhav CD
4

Reimus gab die richtige Antwort, aber ich würde gerne näher darauf eingehen.

In Java (und den meisten Sprachen) ist die Konvention variabel links und die Zuweisung rechts.

Lassen Sie es uns zusammenfassen:

String x = "hello";
//x <- "hello"

String y = "goodbye";
//y <- "goodbye";

Für Debugging-Zwecke sowie zur Lesbarkeit des Codes ist es immer eine gute Praxis, Ihre Zeilen so aufzuteilen, dass sie nur eines tun.

System.out.println(x.equals(x = y)); //Compound statement

Hier x.equals(...)wird auf die ursprüngliche Referenz zu x oder "Hallo" aufgerufen, es wird für die zweite Referenz aktualisiert.

Ich würde dies schreiben als (und dies wird Ihnen Ihre erwartete Antwort geben):

x = y;
// x <- y = "goodbye"

boolean xEqualsX = x.equals(x);
// xEqualsX <- true

System.out.println(xEqualsX);
// "true"

Nun scheint es offensichtlich, dass es sich so verhalten sollte, aber es ist auch sehr einfach, genau zu sehen, was in jeder Zeile vor sich geht, was Sie anstreben sollten.

Randomness Slayer
quelle
2
Betreff: "immer": Es gibt so etwas wie zu viel explizite System.out.println("Bytes: "+1024*k);Aussage : Würden Sie empfehlen, dass drei Aussagen geschrieben werden?
Davis Herring
@DavisHerring Dies ist im Zusammenhang mit dem Debuggen. Wenn Sie versuchen, diese Anweisung zu debuggen, dann würde ich absolut empfehlen, diese Anweisung in ihre Komponenten aufzuteilen. Eine Aussage für die Multiplikation und eine andere für den Druck. Es ermöglicht maximale Flexibilität. Als allgemeine Faustregel möchten Sie, dass jede Zeile nur eines tut. Es macht Code lesbarer und leichter zu debuggen.
Randomness Slayer
2

Ich habe Ihre Frage in Eclipse versucht, Ihre beiden Ausdrücke sind korrekt. 1) x == (y = x) als wahr auswerten, es ist wahr, weil der Wert von x y zuweist, was 'Hallo' ist, dann werden x und y verglichen, sie werden gleich sein, so dass das Ergebnis wahr ist

2) x.equal (x = y) ist falsch, weil der Wert von y x zugewiesen wird, was auf Wiedersehen ist, dann sind x und x, die ihren Wert vergleichen, unterschiedlich, so dass das Ergebnis falsch ist

Alishan
quelle
1

Ich sehe die Frage in Laienbegriffen als "hello".equals("goodbye"). Es wird also false zurückgegeben.

Vamsi
quelle
1

In Java ist String eine Klasse.

String x = "hello";
String y = "goodbye"; 

ist eine zwei verschiedene Zeichenfolge, die sich auf zwei verschiedene Werte bezieht, die nicht gleich sind und wenn Sie vergleichen

 System.out.println(x.equals(x = y)); 
//this compare value (hello and goodbye) return true

    System.out.println(x == (y = x)); 
// this compare reference of an object (x and y) return false  
Aadesk
quelle
1
Haben Sie diesen Beispielcode tatsächlich ausgeführt? Wie in der Frage angegeben, System.out.println(x.equals(x = y));wird im Gegensatz zu Ihrer Antwort false zurückgegeben.
Charlie Harding
-4

Es wird angezeigt, ob x.equals (y y zuweisen, immer true zurückgeben), also im Grunde x.equals (true)

mmx
quelle
8
Das ist einfach falsch, so wird die Aufgabe nicht bewertet
Sam