15.25.2 Compound Assignment Operators

All compound assignment operators require both operands to be of primitive type, except for +=, which allows the right-hand operand to be of any type if the left- hand operand is of type String.

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. Note that the implied cast to type T may be either an identity conversion (§5.1.1) or a narrowing primitive conversion (§5.1.3). For example, the following code is correct:


short x = 3;
x += 4.6;

and results in x having the value 7 because it is equivalent to:


short x = 3;
x = (short)(x + 4.6);

At run time, the expression is evaluated in one of two ways. If the left-hand operand expression is not an array access expression, then four steps are required:

If the left-hand operand expression is an array access expression (§15.12), then many steps are required:

The rules for compound assignment to an array component are illustrated by the following example program:


class ArrayReferenceThrow extends RuntimeException { }

class IndexThrow extends RuntimeException { }

class RightHandSideThrow extends RuntimeException { }
class IllustrateCompoundArrayAssignment {
static String[] strings = { "Simon", "Garfunkel" };

static double[] doubles = { Math.E, Math.PI };


	static String[] stringsThrow() {
		throw new ArrayReferenceThrow();
	}

static double[] doublesThrow() { throw new ArrayReferenceThrow(); }

static int indexThrow() { throw new IndexThrow(); }
static String stringThrow() { throw new RightHandSideThrow(); }
static double doubleThrow() { throw new RightHandSideThrow(); }
static String name(Object q) { String sq = q.getClass().getName(); int k = sq.lastIndexOf('.'); return (k < 0) ? sq : sq.substring(k+1); }
static void testEight(String[] x, double[] z, int j) { String sx = (x == null) ? "null" : "Strings"; String sz = (z == null) ? "null" : "doubles"; System.out.println(); try { System.out.print(sx + "[throw]+=throw => "); x[indexThrow()] += stringThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sz + "[throw]+=throw => "); z[indexThrow()] += doubleThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sx + "[throw]+=\"heh\" => "); x[indexThrow()] += "heh"; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sz + "[throw]+=12345 => "); z[indexThrow()] += 12345; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sx + "[" + j + "]+=throw => "); x[j] += stringThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sz + "[" + j + "]+=throw => "); z[j] += doubleThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sx + "[" + j + "]+=\"heh\" => "); x[j] += "heh"; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print(sz + "[" + j + "]+=12345 => "); z[j] += 12345; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } }
public static void main(String[] args) { try { System.out.print("throw[throw]+=throw => "); stringsThrow()[indexThrow()] += stringThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[throw]+=throw => "); doublesThrow()[indexThrow()] += doubleThrow(); System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); } try { System.out.print("throw[throw]+=\"heh\" => "); stringsThrow()[indexThrow()] += "heh"; System.out.println("Okay!"); } catch (Throwable e) { System.out.println(name(e)); }


		try {
			System.out.print("throw[throw]+=12345 => ");
			doublesThrow()[indexThrow()] += 12345;
			System.out.println("Okay!");
		} catch (Throwable e) { System.out.println(name(e)); }
		try {
			System.out.print("throw[1]+=throw => ");
			stringsThrow()[1] += stringThrow();
			System.out.println("Okay!");
		} catch (Throwable e) { System.out.println(name(e)); }
		try {
			System.out.print("throw[1]+=throw => ");
			doublesThrow()[1] += doubleThrow();
			System.out.println("Okay!");
		} catch (Throwable e) { System.out.println(name(e)); }
		try {
			System.out.print("throw[1]+=\"heh\" => ");
			stringsThrow()[1] += "heh";
			System.out.println("Okay!");
		} catch (Throwable e) { System.out.println(name(e)); }
		try {
			System.out.print("throw[1]+=12345 => ");
			doublesThrow()[1] += 12345;
			System.out.println("Okay!");
		} catch (Throwable e) { System.out.println(name(e)); }

		testEight(null, null, 1);
		testEight(null, null, 9);
		testEight(strings, doubles, 1);
		testEight(strings, doubles, 9);
	}

}

This program prints:


throw[throw]+=throw => ArrayReferenceThrow
throw[throw]+=throw => ArrayReferenceThrow
throw[throw]+="heh" => ArrayReferenceThrow
throw[throw]+=12345 => ArrayReferenceThrow
throw[1]+=throw => ArrayReferenceThrow
throw[1]+=throw => ArrayReferenceThrow
throw[1]+="heh" => ArrayReferenceThrow
throw[1]+=12345 => ArrayReferenceThrow

null[throw]+=throw => IndexThrow null[throw]+=throw => IndexThrow null[throw]+="heh" => IndexThrow null[throw]+=12345 => IndexThrow null[1]+=throw => NullPointerException null[1]+=throw => NullPointerException null[1]+="heh" => NullPointerException null[1]+=12345 => NullPointerException
null[throw]+=throw => IndexThrow null[throw]+=throw => IndexThrow null[throw]+="heh" => IndexThrow null[throw]+=12345 => IndexThrow null[9]+=throw => NullPointerException null[9]+=throw => NullPointerException null[9]+="heh" => NullPointerException null[9]+=12345 => NullPointerException
Strings[throw]+=throw => IndexThrow doubles[throw]+=throw => IndexThrow Strings[throw]+="heh" => IndexThrow doubles[throw]+=12345 => IndexThrow Strings[1]+=throw => RightHandSideThrow doubles[1]+=throw => RightHandSideThrow Strings[1]+="heh" => Okay! doubles[1]+=12345 => Okay!
Strings[throw]+=throw => IndexThrow doubles[throw]+=throw => IndexThrow Strings[throw]+="heh" => IndexThrow doubles[throw]+=12345 => IndexThrow Strings[9]+=throw => IndexOutOfBoundsException doubles[9]+=throw => IndexOutOfBoundsException Strings[9]+="heh" => IndexOutOfBoundsException doubles[9]+=12345 => IndexOutOfBoundsException

The most interesting cases of the lot are tenth and eleventh from the end:


Strings[1]+=throw => RightHandSideThrow
doubles[1]+=throw => RightHandSideThrow

They are the cases where a right-hand side that throws an exception actually gets to throw the exception; moreover, they are the only such cases in the lot. This demonstrates that the evaluation of the right-hand operand indeed occurs after the checks for a null array reference value and an out-of-bounds index value.

The following program illustrates the fact that the value of the left-hand side of a compound assignment is saved before the right-hand side is evaluated:


class Test {
	public static void main(String[] args) {
		int k = 1;
		int[] a = { 1 };
		k += (k = 4) * (k + 2);
		a[0] += (a[0] = 4) * (a[0] + 2);
		System.out.println("k==" + k + " and a[0]==" + a[0]);
	}
}

This program prints:

k==25 and a[0]==25

The value 1 of k is saved by the compound assignment operator += before its right-hand operand (k = 4) * (k + 2) is evaluated. Evaluation of this right-hand operand then assigns 4 to k, calculates the value 6 for k + 2, and then multiplies 4 by 6 to get 24. This is added to the saved value 1 to get 25, which is then stored into k by the += operator. An identical analysis applies to the case that uses a[0]. In short, the statements


k += (k = 4) * (k + 2);
a[0] += (a[0] = 4) * (a[0] + 2);

behave in exactly the same manner as the statements:


k = k + (k = 4) * (k + 2);
a[0] = a[0] + (a[0] = 4) * (a[0] + 2);