jordanpg
jordanpg

Reputation: 6516

In Java, why does an assignment in parentheses not occur before the rest of the expression is evaluated?

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

Answers (4)

John
John

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

arshajii
arshajii

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

Tadas S
Tadas S

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

Sotirios Delimanolis
Sotirios Delimanolis

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

Related Questions