8

In the following example what actually happens?

int a = 1;
a += (a = 2);

The output is 3, however I wanted to know what actually happens under the covers. For example i know that parentheses have higher priority to + so happening first (a = 2) the expression should become a = 2 + 2. At runtime first the expression within parentheses should be executed and then a becomes 2. It seems that the first a on the left to + gets "loaded" before of (a = 2) and this last expression does not seem to override the previous loading. In other words I am quite confused to what exactly happens behind the scenes.

If anybody knows, thanks a lot in advance.

Rollerball
  • 12,618
  • 23
  • 92
  • 161

3 Answers3

4

From the JLS section §15.26.2 Compound Assignment Operators:

A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T)((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.

So for your example we have:

a = (a) + (a = 2)

With the expression evaluated left to right. Hence the output of 3

Rich O'Kelly
  • 41,274
  • 9
  • 83
  • 114
  • The statement you quoted doesn't say anything about the order of evaluation. – nhahtdh Mar 08 '13 at 21:14
  • @rich.okelly ok so basically the left (a) gets put in turn in parentheses implicitly? – Rollerball Mar 08 '13 at 21:19
  • @nhahtdh It is inferred - the order of precedence is guaranteed by the ordering in the statement with brackets disambiguating statements. – Rich O'Kelly Mar 08 '13 at 21:21
  • @Rollerball Yes, but it doesn't affect the outcome of the statement. The brackets there are redundant. The right hand side of the `=` operator is evaluated left to right – Rich O'Kelly Mar 08 '13 at 21:23
  • @rich.okelly: By your line of reasoning, it is better to say that the order is guaranteed by the fact that all valid `op` are evaluated from left to right. – nhahtdh Mar 08 '13 at 21:32
  • @nhahtdh Yes, I thought that the reason would be apparent once the expansion of the `+=` operator was stated. My bad! – Rich O'Kelly Mar 08 '13 at 21:44
2

See the referenced example 15.7.1-2 from http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.7.1, which is almost identical to the example you provided. In particular:

If the operator is a compound-assignment operator (§15.26.2), then evaluation of the left-hand operand includes both remembering the variable that the left-hand operand denotes and fetching and saving that variable's value for use in the implied binary operation.

Because of this precedence, the left hand of the += is evaluated first.

It might be confusing to you because of the parentheses, but note the section on parenthesis evaluation: http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.7.3, and in particular:

The Java programming language respects the order of evaluation indicated explicitly by parentheses and implicitly by operator precedence.

In this case, the implicit precedence set by the += operator indicates that the left hand operand will be remembered per the spec. While it's true that assignment operators, including "+=" have lowest precedence, the spec for += indicates that the left-hand operand will be remembered per 15.26.2.

Kirby
  • 3,649
  • 3
  • 26
  • 28
  • 1
    actually the priority of += is the least and should be evaluated last – Rollerball Mar 08 '13 at 21:25
  • Please read the text of the example: In the following program, the two assignment statements both fetch and remember the value of the left-hand operand, which is 9, before the right-hand operand of the addition operator is evaluated, at which point the variable is set to 3. – Kirby Mar 08 '13 at 21:27
  • This example is the same as yours, but with different numbers. – Kirby Mar 08 '13 at 21:27
2

Let's take a look at the bytecode of the following program:

package A;

public class Test
{
    public static void main(String[] args)
    {
        int a = 1;
        a += (a = 2);
    }
}

We just need to run this command:

javap -c Test.class

to get the following bytecode:

public class A.Test {
  public A.Test();
    Code:
       0: aload_0
       1: invokespecial #1           // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: istore_1
       2: iload_1
       3: iconst_2
       4: dup
       5: istore_1
       6: iadd
       7: istore_1
       8: return
}

Explanation:

We will just focus on the two lines inside the main method:

int a = 1;
a += (a = 2);

[int a = 1; begins here]

0: iconst_1
  • Pushes int 1 onto the stack.
-------------
|           |
-------------
|           |
-------------
|     1     |
-------------
    STACK

1: istore_1
  • Pops int value from the stack to variable 1 (variable 1 represents a)
-------------
|           |             variable 1
-------------           --------------
|           |           |     1      |
-------------           --------------
|           |
-------------
    STACK

[int a = 1; finishes here]


[a += (a = 2); begins here]

2: iload_1
  • Loads an int value from local variable 1 and pushes it onto the stack.
-------------
|           |             variable 1
-------------           --------------
|           |           |            |
-------------           --------------
|     1     |
-------------
    STACK

3: iconst_2
  • Pushes int 2 onto the stack.
-------------
|           |             variable 1
-------------           --------------
|     2     |           |            |
-------------           --------------
|     1     |
-------------
    STACK

4: dup
  • duplicate the value on top of the stack.
-------------
|     2     |             variable 1
-------------           --------------
|     2     |           |            |
-------------           --------------
|     1     |
-------------
    STACK

5: istore_1
  • Pops int value from the stack to variable 1.
-------------
|           |             variable 1
-------------           --------------
|     2     |           |      2     |
-------------           --------------
|     1     |
-------------
    STACK

6: iadd
  • Adds the top two values together.
-------------
|           |             variable 1
-------------           --------------
|           |           |      2     |
-------------           --------------
|     3     |
-------------
    STACK

7: istore_1
  • Pops int value from the stack to variable 1.
-------------
|           |             variable 1
-------------           --------------
|           |           |      3     |
-------------           --------------
|           |
-------------
    STACK

[a += (a = 2); finishes here]


8: return
  • The main method returns.

Conclusion:

a = a + (a = 2) is done through several operations. 2: iload_1 is executed as first command of a += (a = 2); which reads the first operand of the equation a = a + (a = 2) and pushes onto the stack.

Next, 3: iconst_2 and 4: dup are executed which basically push int 2 twice onto the stack; one for loading it to a and the other as the second operand. After that, 5: istore_1 is executed which is loading 2 into a (a = 2).

Finally, 6: iadd and 7: istore_1 are executed where 6: iadd adds the first operand and the second operand and pushes the result onto the stack, and 7: istore_1 pops the result and loads it into a.


For simplicity, let's have a quick look at this code:

int a = 1;
int b = 3;
a += b;

and here is its bytecode:

public class A.Test {
  public A.Test();
    Code:
       0: aload_0
       1: invokespecial #1            // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_3
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: istore_1
       8: return
}

As you can see, it simply does the following:

  • Loads int 1 into a.
  • Loads int 3 into b.
  • Pushes a then b onto the stack.
  • Performs the addition on them and pushes the result onto the stack.
  • Pops the result from the stack and stores it into a.
Eng.Fouad
  • 115,165
  • 71
  • 313
  • 417