Java assignment operator execution
Asked Answered
S

9

77

In Java, I understand that assignment evaluates to the value of the right operand, so statements like x == (y = x) evaluate to true.

This code, however, outputs false.

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

Why is this? In my understanding, it first evaluates (x = y), which assigns x the value of y, and then returns the value of y. Then x.equals(y) is evaluated, which should be true since x and y should share the same references now, but instead, I get false.

Screenshot showing the source and that the output is "false"

What is happening here?

Sycamore answered 21/6, 2018 at 13:40 Comment(7)
I think you wanted to see result for x.equals( y = x )Boehmenism
Could the compiler inline x and y?Cordero
Are you assuming that the assignment x = y on the right hand side is executed before the x on the left hand side is evaluated?Pierian
@Pierian yes, that was my assumption. It must notSycamore
Possible duplicate of Order of execution of methods describing an instance and an argument in Java?Nels
Related: 1 2Nels
@Boehmenism I don't think so. OP already said they understand that x == (y = x) evaluates to true. The behavior of what you suggest would then be obvious...Circumstantiality
D
76

First of all: that's an interesting question, but should never come up in "real code", as assigning to the variable you call in the very same line is confusing even if you know how it works.

What happens here is these 3 steps:

  1. figure out which object to call the method on (i.e. evaluate the first x, this will result in a reference to the String "hello")
  2. figure out the parameters (i.e. evaluate x = y, which will change x to point to the String "goodbye" and also return a reference to that String)
  3. call the method equals on the result of #1 using the result of #2 as the parameter (which will be references to the Strings "hello" and "goodbye" respectively).

Looking at the byte code produced for that method makes it clear (assuming you're fluent in 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

Line #9 is step 1 above (i.e. evaluates x and remembers the value).

Line #10-12 is step 2. It loads y, duplicates it (once for assigning, once for the return value of the assignment expression) and assigns it to x.

Line #13 invokes equals on the result computed in Line #9 and the result of Lines #10-12.

Dropwort answered 21/6, 2018 at 13:45 Comment(3)
TL;DR: x.equals(x = y) => "hello".equals(x = y) => "hello".equals(x = "goodbye") => "hello".equals("goodbye") => false.Toulouse
An important point to note is that . has a higher precedence than =.Wellbalanced
This is more about evaluation order than about precedence. The parentheses make the precedence irrelevant anyway.Umont
P
38

Good question! And the JLS has the answer...

§15.12.4.1 (Example 15.12.4.1-2). Evaluation Order During Method Invocation:

As part of an instance method invocation, there is an expression that denotes the object to be invoked. This expression appears to be fully evaluated before any part of any argument expression to the method invocation is evaluated.

So, in:

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

the occurrence of x before .equals is evaluated first, before the argument expression x = y.

Therefore,  a reference to the string  hello  is remembered as the target reference before the local variable x is changed to refer to the string goodbye. As a result, the equals method is invoked for target object hello with argument goodbye, so the result of the invocation is false.

Phosphate answered 21/6, 2018 at 18:10 Comment(0)
M
29

It's important to remember that a String in java is an object, and therefore a reference. When you call

x.equals(...)

It is checking if the value at the location currently referenced by x is equal to what you are passing in. Inside, you are changing the value that x is referencing, but you are still calling equals with the original reference (the reference to "hello"). So, right now your code is comparing to see if "hello" is equal to "goodbye", which it clearly is not. After this point, if you use x again, it will result in a reference to the same value as y.

Marguritemargy answered 21/6, 2018 at 13:47 Comment(2)
if i understand you, this: "hello".equals((x = y)) should return true ?Kansas
No, because (x = y) will return the value of y, which is "goodbye". So if you did "goodbye".equals(x=y), that would return trueMarguritemargy
S
5

x=y in the parenthesis means that expression (x=y) is now goodbye, while the outer x in x.equals holds the value hello

Suburbanite answered 21/6, 2018 at 13:46 Comment(2)
This really doesn't explain why this is happening nor does it provide additional details that would be helpful for others.Gonorrhea
Now that I read it out loud, I do tend to agree with you..the other answers are pretty verbose tho so dint edit it..Suburbanite
C
4

Reimus gave the correct answer, but I'd like to elaborate.

In Java (and most languages) the convention is variable goes on the left, assignment on the right.

Let's Break it down:

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

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

For debugging purposes as well as code readability, it's always a good practice to split up your lines so that they only do one thing.

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

Here, x.equals(...) is called on the original reference to x, or "hello", it is updated for the second reference.

I would write this as (and this will give you your expected answer):

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

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

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

Now this seems obvious that it should behave this way, but it's also really easy to see exactly what is going on in each line, which is something you should strive for.

Colcannon answered 21/6, 2018 at 13:57 Comment(2)
Re: "always": there's such a thing as too much explicitness: would you recommend that System.out.println("Bytes: "+1024*k); be written as three statements?Kuster
@DavisHerring this is in the context of debugging. If you are trying to debug that statement, then yes, I would absolutely recommend splitting this statement into it's components. One statement for the multiplication and another for print. It allows for maximum flexibility. As a general rule of thumb, you want each line to do only one thing. It makes code more readable and more easily debug-able.Colcannon
C
2

I see the question in layman terms as "hello".equals("goodbye"). So it returns false.

Claro answered 22/6, 2018 at 19:5 Comment(0)
I
2

I have tried your question in eclipse your both expression are correct. 1) x == (y = x) evaluate to true it is true because value of x assign to y which is 'hello' then x and y compare they will same so result will true

2) x.equal(x = y) it is false because value of y assign to x which is goodbye then x and x compare their value will be different so result will be false

Intransigeance answered 27/6, 2018 at 20:48 Comment(0)
M
1

In java String is a class.

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

is a two different String which refer to two different value which is not same and if you compare

 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  
Magdala answered 28/6, 2018 at 6:11 Comment(1)
Have you actually run this sample code? As the question states, System.out.println(x.equals(x = y)); returns false, contrary to what your answer claims.Biomedicine
I
-5

It is seeing if x.equals (assign x to y, returns true always) so basically x.equals(true)

Infracostal answered 22/6, 2018 at 19:14 Comment(1)
this is just untrue, that's not how the assignment is evaluatedSycamore

© 2022 - 2024 — McMap. All rights reserved.