Reputation: 6516
Consider
int a = 20;
a = a + (a = 5); // a == 25, why not 10?
Don't parentheses trump all precedence rules? Are some variables on the RHS prepopulated before evaluation of certain expressions?
Upvotes: 5
Views: 317
Reputation: 751
You asked why and the latest JLS provides a clearer explanation of what is happening in JLS 17:
In particular, the presence or absence of parentheses around an expression does not affect whether a variable is definitely assigned, definitely assigned when true, definitely assigned when false, definitely unassigned, definitely unassigned when true, or definitely unassigned when false.
In effect they are saying that parentheses do not prompt assignment, which explains why assignment occurs left-to-right, unlike evaluation, which occurs in innermost-to-outermost order.
But that still does not answer why.
It's not because of compatibility with Java's predecessor, at least not in its present form, because the behavior does not match C:
Java 17:
int j = 3, k = 4, l = 5, m = 6, n = 7;
System.out.println(((j = 3) + j) + j); // 3 + 3 + 3 = 9
System.out.println(k + (k + (k = 3))); // 4 + 4 + 3 = 11
System.out.println(l + ((l = 3) + l)); // 5 + 3 + 3 = 11
System.out.println((m + (m += 3)) + m); // 6 + 9 + 9 = 24
System.out.println(n + (n += n / 2) + ++n); // 7 + 11 + 12 = 28
GCC 7.5.0:
printf("%d\n",((j = 3) + j) + j); // 3 + 3 + 3 = 9
printf("%d\n",k + (k + (k = 3))); // 3 + 3 + 3 = 9
printf("%d\n",l + ((l = 3) + l)); // 3 + 3 + 3 = 9
printf("%d\n",(m + (m += 3)) + m); // 9 + 9 + 9 = 27
printf("%d\n",n + (n += n / 2) + ++n); // 10 + 10 + 11 = 31
This leaves us with the explanation used elsewhere for Java awkwardness: upgradeability. Java happened to do it this way at the time it was written; changing it to work in alignment with C would potentially break existing code. I'd be curious to know of any discussion that might have taken place when the initial design was decided.
Upvotes: 1
Reputation: 129497
Because a
is loaded first in the example you have, and then the bit in parenthesis is evaluated. If you reversed the order:
int a = 20;
a = (a = 5) + a;
System.out.println(a);
10
... you do indeed get 10. Expressions are evaluated from left to right.
Consider this:
f() + g()
f
will be called before g
. Imagine how unintuitive it would be, in
f() + (g())
to have g
be called before f
.
This is all detailed in JLS §15.7.1 (thanks to @paisanco for bringing it up in the comments).
Upvotes: 7
Reputation: 1962
Generated bytecode:
BIPUSH 20
ISTORE 1
ILOAD 1
ICONST_5
DUP
ISTORE 1
IADD
ISTORE 1
RETURN
LOCALVARIABLE a I
First, you assign 20 to the first variable (a):
BIPUSH 20
ISTORE 1
Then, you load the contents of the first variable to stack (20 is put on stack):
ILOAD 1
Then, you push the constant '5' to the stack twice (20 5 5):
ICONST_5
DUP
Then, you store the top of the stack to the first variable (a):
ISTORE 1
a is now 5, stack is now (20 5). We add both operands and put their sum to the first variable (a):
IADD
ISTORE 1
As a consequence, a is now 20 + 5 = 25. We end:
RETURN
Upvotes: 2
Reputation: 279910
From the JLS
The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.
and
The left-hand operand of a binary operator appears to be fully evaluated before any part of the right-hand operand is evaluated.
Wrapping an expression in parentheses just helps grouping (and associativity), it doesn't force its evaluation to happen before anything to its left.
Upvotes: 5