JAVA / OPERATORS
Assignment and compound assignment shortcuts
Use +=, -=, *=, /= and friends correctly by predicting the hidden cast, the grouped right-hand side, and the single evaluation of the target.
What you will learn
- Read x += e as x = (type of x)(x + e), narrowing cast included
- Use the compound form when the target has side effects: it is evaluated only once
- Predict that x *= y + 1 multiplies by y + 1, because the right side is grouped first
- Chain a = b = 0 knowing assignment groups right to left and yields the value it stored
Understanding Assignment and compound assignment shortcuts
The equals sign in Java is an operator, not punctuation. It evaluates the expression on its right, stores that value into the variable, field, or array element on its left, and then produces the stored value as its own result. Because assignment groups right to left, a = b = c = 5 runs c = 5 first, hands back 5, and passes it leftward, so all three variables end up holding 5. The same rule makes System.out.println(n = 7) legal: the assignment is an expression, and there is a value there to print.
The eleven compound operators (+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=, >>>=) all follow one rule: x op= e means x = (T)(x op e), where T is the declared type of x. Two things follow from that definition. First, the whole of e is computed before op is applied, so value *= scale + 1 multiplies by scale + 1 and never by scale alone. Second, the result is cast back to T for you with no warning, which is why int rounded = 7; rounded *= 0.5; leaves 3, and why a byte counter can quietly wrap past 127.
The target is also evaluated exactly once. That is invisible for a plain local variable, but counts[index()] += 5 calls index() a single time while counts[index()] = counts[index()] + 5 calls it twice, and with a moving index the two forms write to different elements. So compound assignment is not merely typing shorthand: it changes the number of evaluations and the conversions applied, and those two differences are where the short form and the long form stop being interchangeable.
public class CompoundAssignment {
public static void main(String[] args) {
int total = 10;
total += 5; // total = total + 5
total *= 2; // total = total * 2
total -= 4; // total = total - 4
System.out.println("total = " + total);
int scale = 3;
int value = 4;
value *= scale + 1; // 4 * (3 + 1), not 4 * 3 + 1
System.out.println("value = " + value);
int truncated = 7;
truncated /= 2; // int division: 3
System.out.println("truncated = " + truncated);
int rounded = 7;
rounded *= 0.5; // (int)(7 * 0.5) -> (int) 3.5
System.out.println("rounded = " + rounded);
byte small = 100;
small += 50; // (byte)(100 + 50) wraps
System.out.println("small = " + small);
}
}A compound assignment x op= e means x = (type of x)(x op e), so the target is read once and the result is silently cast back to the target's type.
Worked examples
The target is evaluated once
Shows that the compound form runs the array subscript expression a single time while the long form runs it twice.
public class EvaluateOnce {
static int calls = 0;
static int index() {
calls++;
return 1;
}
public static void main(String[] args) {
int[] counts = {10, 20, 30};
counts[index()] += 5;
System.out.println("compound: counts[1] = " + counts[1] + ", index() calls = " + calls);
calls = 0;
counts[index()] = counts[index()] + 5;
System.out.println("long form: counts[1] = " + counts[1] + ", index() calls = " + calls);
}
}Example explained
Line 1counts[index()] += 5 evaluates the subscript expression once, so calls ends at 1.
Line 2The long form names counts[index()] twice, so index() runs twice and calls ends at 2.
Line 3Both statements happen to hit element 1 here only because index() always returns 1; a counter that changed on each call would make the long form read one slot and write another.
Line 4The printed element grows from 25 to 30 because the first statement had already added 5.
Assignment as an expression
Demonstrates right-to-left grouping of assignment and the fact that an assignment produces the value it stored.
public class ChainedAssignment {
public static void main(String[] args) {
int a, b, c;
a = b = c = 5;
System.out.println(a + " " + b + " " + c);
b += a = 2;
System.out.println("a = " + a + ", b = " + b);
int n = 0;
System.out.println("value of the assignment: " + (n = 7));
System.out.println("n = " + n);
}
}Example explained
Line 1a = b = c = 5 groups as a = (b = (c = 5)), so each assignment hands its stored value to the one on its left.
Line 2b += a = 2 keeps b's old value 5, then evaluates a = 2, and the 2 that assignment yields is what gets added, leaving b as 7.
Line 3Putting (n = 7) inside println prints 7 because the assignment itself is an expression with a value.
Line 4n prints 7 afterwards, confirming the store really happened and was not just an argument conversion.
Compound assignment on char and String
Shows how the same definition applies when the target is a String or a char rather than an int.
public class ShortcutsOnOtherTypes {
public static void main(String[] args) {
String label = "row";
label += 1 + 2;
System.out.println(label);
String tally = "";
tally += 'x';
tally += 3;
System.out.println(tally);
char letter = 'a';
letter += 2;
System.out.println(letter);
int code = 'a';
code += 2;
System.out.println(code);
}
}Example explained
Line 1label += 1 + 2 computes 1 + 2 as int arithmetic before appending, so the result is row3 and not row12.
Line 2tally += 'x' works because String plus char is concatenation; the compound form uses exactly the + that the long form would.
Line 3letter += 2 adds in int and then casts 99 back to char, so the printed character is c.
Line 4code += 2 does the same arithmetic, but the target is an int, so 99 is printed as a number.
Important notes
Compound assignment is the one everyday place where Java narrows a value without an explicit cast; if you want the compiler to object, spell out x = x op e.
The left side must be a variable, field, or array element, so getCount() += 1 does not compile: a method call result is not a storage location.
Common mistakes
Reading total *= rate + fee as total = total * rate + fee: the right side is grouped first, so the fee gets multiplied in rather than added, and the number is silently wrong.
Typing x =+ 5 instead of x += 5: that parses as x = (+5), so x is overwritten with 5 instead of increased, and it compiles without complaint.
Assuming int count = 1; count += 0.9; is an error or rounds up: the hidden cast truncates back to 1, so a loop that advances this way never terminates.
Try it yourself
Change, predict, then run
In a browser editor set int budget = 25; and apply budget *= 0.9; twice, printing budget after each step. Repeat the same two steps with a double variable and add a comment explaining where the difference comes from.
Open the Java workspaceCheck your understanding
byte b = 100; b += 50; compiles and prints -106, but byte b = 100; b = b + 50; does not compile at all. What accounts for the difference?
- b += 50 is defined as b = (byte)(b + 50), so the narrowing cast is supplied automatically
- b += 50 performs the addition in byte width, so no int result is ever produced
- b += 50 is folded at compile time, so the compiler already knows the final value fits
- b + 50 produces a long, and long can never be assigned to a byte
Show answer
The language defines b += 50 as b = (byte)(b + 50), so the cast that the long form is missing is inserted for you. The second option is tempting but wrong: both forms add as int, and the wrap to -106 is exactly the evidence that an int 150 was computed and then narrowed to byte.