skip to content

What are compound assignment operators like += and -=, and how does `x += y` differ from `x = x + y`?

level: juniorimportance: should knowfreq 50%

answer

  1. x op= y ≈ x = (T)(x op y), T = type of x
  2. left operand evaluated ONCE
  3. hidden narrowing cast → byte b; b += 1 compiles
  4. operators: += -= *= /= %= &= |= ^= <<= >>= >>>=
  5. can silently overflow/truncate small types

basics

~20 s

Compound operators combine an operation with assignment: x += y means roughly x = x + y. Java has +=, -=, *=, /=, %=, and the bitwise/shift ones. They are shorthand and evaluate the left side only once.

solid answer

~50 s

Java's compound assignment operators (+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=, >>>=) apply a binary operation between the left-hand side and the right-hand side and store the result back into the left. So `x += y` behaves like `x = x + y`, but with two important differences. First, the left operand is evaluated only once — important when it has side effects, like `arr[idx()] += 1`, where `idx()` runs a single time. Second, and most importantly, the JLS specifies that a compound assignment includes an *implicit cast* of the result back to the type of the left operand. That means `x += y` is really `x = (T)(x + y)` where T is x's type. This silent narrowing cast is why `byte b = 10; b += 1;` compiles even though `b = b + 1` does not (the latter produces an int). It also means a compound assignment can silently truncate or overflow without a compile error.

code

java · 8 lines
java
byte b = 10;
// b = b + 1; // does NOT compile: int -> byte needs a cast
b += 1;       // compiles: equivalent to b = (byte)(b + 1)
System.out.println(b); // 11

byte c = 100;
c += 50;      // overflow, silent: (byte)150
System.out.println(c); // -106

go deeper

for a junior

Knows += etc. are shorthand for applying an operation and assigning the result.

for a middle

States the once-evaluated LHS and that x op= y is x = (T)(x op y) with an implicit cast.

for a senior

Explains why byte/short/char compound assignment compiles via the hidden narrowing cast and the silent overflow risk.

for a principal

Discusses JLS §15.26.2 semantics, side-effect ordering of the LHS, and code-review heuristics for hidden truncation in numeric code.

## The list of compound operators A **compound assignment operator** fuses a binary operation with assignment. Java provides one for each relevant binary operator: | Operator | Meaning | |---|---| | `+=` | add (or String concat) | | `-=` | subtract | | `*=` | multiply | | `/=` | divide | | `%=` | remainder | | `&=` `\|=` `^=` | bitwise AND / OR / XOR (or boolean logical) | | `<<=` `>>=` `>>>=` | left / arithmetic-right / unsigned-right shift | ## The naive mental model Most people learn `x op= y` means `x = x op y`. That's *almost* right and is fine for everyday `int` math. But the Java Language Specification (JLS §15.26.2) defines it more precisely, and the difference matters. ## Difference 1: the left operand is evaluated exactly once In `x = x + y`, the LHS `x` textually appears twice. If the LHS is a complex expression with **side effects**, that matters. Consider `a[next()] += 5`. With compound assignment, `next()` is called **once**: its result picks the array slot, that slot is read, 5 is added, and the result is written back to the *same* slot. If you naively expanded it to `a[next()] = a[next()] + 5`, `next()` would run **twice**, possibly hitting two different slots. So compound assignment is not a pure textual macro. ## Difference 2: the implicit narrowing cast (the big one) The JLS says `E1 op= E2` is equivalent to: ``` E1 = (T)((E1) op (E2)) ``` where **T is the type of E1**, and E1 is evaluated only once. The crucial part is **`(T)`** — an automatic cast of the result back to the left operand's type. Why does this matter? Java's binary numeric operators **promote** small integer types to `int` before operating. So `b + 1` where `b` is a `byte` produces an `int`. Therefore: ``` byte b = 10; b = b + 1; // ERROR: int cannot be assigned to byte without a cast b += 1; // OK: compiles to b = (byte)(b + 1) ``` The compound form inserts the `(byte)` cast for you. Convenient — but it means the compiler will **silently allow narrowing that could lose data or overflow**: ``` byte b = 100; b += 50; // 150 overflows a byte → b becomes -106, no warning ``` ## Difference 3: works with `String +=` `s += x` concatenates: `s = s + String.valueOf(x)`. It accepts any operand type because `+` with a String means concatenation. ## Difference 4: boolean logical compounds `flag &= cond` and `flag |= cond` work on booleans as logical AND/OR-assign. Note `&=`/`|=` on booleans are **non-short-circuiting** — the RHS is always evaluated. ## Practical guidance Use compound assignment for clarity and to evaluate the target once. Be aware it hides a narrowing cast: if `x` is `byte`, `short`, or `char`, a compound assignment can truncate silently where the expanded form would have forced you to acknowledge the cast.

  • Why does `b += 1` compile for a byte but `b = b + 1` does not?
    Binary `+` promotes byte to int, so `b + 1` is an int, and assigning int to byte without a cast is rejected. Compound assignment is defined as `b = (byte)(b + 1)`, inserting the narrowing cast automatically, so it compiles.
  • In `a[i()] += 1`, how many times is `i()` called?
    Exactly once. Compound assignment evaluates the left-hand-side reference/index a single time, then reads, operates, and writes back to that same location.

saying these in an interview costs you the question

  • Claiming x += y is exactly identical to x = x + y in all cases
  • Not knowing about the implicit narrowing cast
  • Thinking the LHS index expression is evaluated twice
  • Assuming compound assignment warns on overflow/truncation

context