Why is x == (x = y) not the same as (x = y) == x?

半腔热情 提交于 2020-01-11 14:49:48

问题


Consider the following example:

class Quirky {
    public static void main(String[] args) {
        int x = 1;
        int y = 3;

        System.out.println(x == (x = y)); // false
        x = 1; // reset
        System.out.println((x = y) == x); // true
     }
}

I'm not sure if there is an item in the Java Language Specification that dictates loading the previous value of a variable for comparison with the right side (x = y) which, by the order implied by brackets, should be calculated first.

Why does the first expression evaluate to false, but the second evaluate to true? I would have expected (x = y) to be evaluated first, and then it would compare x with itself (3) and return true.


This question is different from order of evaluation of subexpressions in a Java expression in that x is definitely not a 'subexpression' here. It needs to be loaded for the comparison rather than to be 'evaluated'. The question is Java-specific and the expression x == (x = y), unlike far-fetched impractical constructs commonly crafted for tricky interview questions, came from a real project. It was supposed to be a one-line replacement for the compare-and-replace idiom

int oldX = x;
x = y;
return oldX == y;

which, being even simpler than x86 CMPXCHG instruction, deserved a shorter expression in Java.


回答1:


which, by the order implied by brackets, should be calculated first

No. It is a common misconception that parentheses have any (general) effect on calculation or evaluation order. They only coerce the parts of your expression into a particular tree, binding the right operands to the right operations for the job.

(And, if you don't use them, this information comes from the "precedence" and associativity of the operators, something that's a result of how the language's syntax tree is defined. In fact, this is still exactly how it works when you use parentheses, but we simplify and say that we're not relying on any precedence rules then.)

Once that's done (i.e. once your code has been parsed into a program) those operands still need to be evaluated, and there are separate rules about how that is done: said rules (as Andrew has shown us) state that the LHS of each operation is evaluated first in Java.

Note that this is not the case in all languages; for example, in C++, unless you're using a short-circuiting operator like && or ||, the evaluation order of operands is generally unspecified and you shouldn't rely on it either way.

Teachers need to stop explaining operator precedence using misleading phrases like "this makes the addition happen first". Given an expression x * y + z the proper explanation would be "operator precedence makes the addition happen between x * y and z, rather than between y and z", with no mention of any "order".




回答2:


== is a binary equality operator.

The left-hand operand of a binary operator appears to be fully evaluated before any part of the right-hand operand is evaluated.

Java 11 Specification > Evaluation Order > Evaluate Left-Hand Operand First




回答3:


As LouisWasserman said, the expression is evaluated left to right. And java doesn't care what "evaluate" actually does, it only cares about generating a (non volatile, final) value to work with.

//the example values
x = 1;
y = 3;

So to calculate the first output of System.out.println(), the following is done:

x == (x = y)
1 == (x = y)
1 == (x = 3) //assign 3 to x, returns 3
1 == 3
false

and to calculate the second:

(x = y) == x
(x = 3) == x //assign 3 to x, returns 3
3 == x
3 == 3
true

Note that the second value will always evaluate to true, regardless of the initial values of x and y, because you are effectively comparing the assignment of a value to the variable it is assigned to, and a = b and b will, evaluated in that order, always be the same by definition.




回答4:


I'm not sure if there is an item in the Java Language Specification that dictates loading the previous value of a variable...

There is. Next time you are unclear what the specification says, please read the specification and then ask the question if it is unclear.

... the right side (x = y) which, by the order implied by brackets, should be calculated first.

That statement is false. Parentheses do not imply an order of evaluation. In Java, the order of evaluation is left to right, regardless of parentheses. Parentheses determine where the subexpression boundaries are, not the order of evaluation.

Why does the first expression evaluate to false, but the second evaluate to true?

The rule for the == operator is: evaluate the left side to produce a value, evaluate the right side to produce a value, compare the values, the comparison is the value of the expression.

In other words, the meaning of expr1 == expr2 is always the same as though you had written temp1 = expr1; temp2 = expr2; and then evaluated temp1 == temp2.

The rule for the = operator with a local variable on the left side is: evaluate the left side to produce a variable, evaluate the right side to produce a value, perform the assignment, the result is the value that was assigned.

So put it together:

x == (x = y)

We have a comparison operator. Evaluate the left side to produce a value -- we get the current value of x. Evaluate the right side: that's an assignment so we evaluate the left side to produce a variable -- the variable x -- we evaluate the right side -- the current value of y -- assign it to x, and the result is the assigned value. We then compare the original value of x to the value that was assigned.

You can do (x = y) == x as an exercise. Again, remember, all the rules for evaluating the left side happen before all the rules of evaluating the right side.

I would have expected (x = y) to be evaluated first, and then it would compare x with itself (3) and return true.

Your expectation is based on a set of incorrect beliefs about the rules of Java. Hopefully you now have correct beliefs and will in the future expect true things.

This question is different from "order of evaluation of subexpressions in a Java expression"

This statement is false. That question is totally germane.

x is definitely not a 'subexpression' here.

This statement is also false. It is a subexpression twice in each example.

It needs to be loaded for the comparison rather than to be 'evaluated'.

I have no idea what this means.

Apparently you still have many false beliefs. My advice is that you read the specification until your false beliefs are replaced by true beliefs.

The question is Java-specific and the expression x == (x = y), unlike far-fetched impractical constructs commonly crafted for tricky interview questions, came from a real project.

The provenance of the expression is not relevant to the question. The rules for such expressions are clearly described in the specification; read it!

It was supposed to be a one-line replacement for the compare-and-replace idiom

Since that one-line replacement caused a great deal of confusion in you, the reader of the code, I would suggest that it was a poor choice. Making the code more concise but harder to understand is not a win. It is unlikely to make the code faster.

Incidentally, C# has compare and replace as a library method, which can be jitted down to a machine instruction. I believe Java does not have such a method, as it cannot be represented in the Java type system.




回答5:


It is related to operator precedence and how operators are getting evaluated.

Parentheses '()' has higher precedence and has associativity left to right. Equality '==' come next in this question and has associativity left to right. Assignment '=' come last and has associativity right to left.

System use stack to evaluate expression. Expression gets evaluated left to right.

Now comes to original question:

int x = 1;
int y = 3;
System.out.println(x == (x = y)); // false

First x(1) will be pushed to stack. then inner (x = y) will be evaluated and pushed to stack with value x(3). Now x(1) will be compared against x(3) so result is false.

x = 1; // reset
System.out.println((x = y) == x); // true

Here, (x = y) will be evaluated, now x value become 3 and x(3) will be pushed to stack. Now x(3) with changed value after equality will be pushed to stack. Now expression will be evaluated and both will be same so result is true.




回答6:


It is not the same. The left hand side will always be evaluated before the right hand side, and the brackets don't specify an order of execution, but a grouping of commands.

With:

      x == (x = y)

You are basically doing the same as:

      x == y

And x will have the value of y after the comparison.

While with:

      (x = y) == x

You are basically doing the same as:

      x == x

After x took y's value. And it will always return true.




回答7:


In the first test you're checking does 1 == 3.

In the second test your checking does 3 == 3.

(x = y) assigns the value and that value is tested. In the former example x = 1 first then x is assigned 3. Does 1 == 3?

In the latter, x is assigned 3, and obviously it's still 3. Does 3 == 3?




回答8:


Consider this other, maybe simpler example:

int x = 1;
System.out.println(x == ++x); // false
x = 1; // reset
System.out.println(++x == x); // true

Here, the pre-increment operator in ++x must be applied before the comparison is made — just like (x = y) in your example must be calculated before the comparison.

However, expression evaluation still happens left → to → right, so the first comparison is actually 1 == 2 while the second is 2 == 2.
The same thing happens in your example.




回答9:


Expressions are evaluated from left to right. In this case:

int x = 1;
int y = 3;

x == (x = y)) // false
x ==    t

- left x = 1
- let t = (x = y) => x = 3
- x == (x = y)
  x == t
  1 == 3 //false

(x = y) == x); // true
   t    == x

- left (x = y) => x = 3
           t    =      3 
-  (x = y) == x
-     t    == x
-     3    == 3 //true



回答10:


Basically the first statement x had it's value 1 So Java compares 1 == to new x variable which won't be the same

In the second one you said x=y which means the value of x changed and so when you call it again it'll be the same value hence why it's true and x ==x




回答11:


== is a comparison equality operator and it works from left to right.

x == (x = y);

here the old assigned value of x is compared with new assign value of x, (1==3)//false

(x = y) == x;

Whereas, here new assign value of x is compared with the new holding value of x assigned to it just before comparison, (3==3)//true

Now consider this

    System.out.println((8 + (5 * 6)) * 9);
    System.out.println(8 + (5 * 6) * 9);
    System.out.println((8 + 5) * 6 * 9);
    System.out.println((8 + (5) * 6) * 9);
    System.out.println(8 + 5 * 6 * 9);

Output:

342

278

702

342

278

Thus, Parentheses plays its major role in arithmetic expressions only not in comparison expressions.




回答12:


The thing here is the arithmatic operators/relational operators precedency order out of the two operators = vs == the dominant one is == (Relational Operators dominates ) as it precedes = assignment operators. Despite precedence, the order of evaluation is LTR (LEFT TO RIGHT) precedence comes into picture after evaluation order. So, Irrespective of any constraints evaluation is LTR.




回答13:


It is easy in the second comparison on the left is assignment after assigning y to x (on the left) you then comparing 3 == 3. In the first example you are comparing x = 1 with new assign x = 3. It seems that there is always taken current state reading statements from left to right of x.




回答14:


The kind of question you asked is a very good question if you want to write a Java compiler, or test programs to verify that a Java compiler is working correctly. In Java, these two expressions must produce the results that you saw. In C++, for example, they don't have to - so if someone reused parts of a C++ compiler in their Java compiler, you might theoretically find that the compiler doesn't behave as it should.

As a software developer, writing code that is readable, understandable and maintainable, both versions of your code would be considered awful. To understand what the code does, one has to know exactly how the Java language is defined. Someone who writes both Java and C++ code would shudder looking at the code. If you have to ask why a single line of code does what it does, then you should avoid that code. (I suppose and hope that the guys who answered your "why" question correctly will themselves avoid that ind of code as well).



来源:https://stackoverflow.com/questions/53749841/why-is-x-x-y-not-the-same-as-x-y-x

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!