Varying behavior for possible loss of precision
That's because b += 1.0;
is equivalent to b = (int) ((b) + (1.0));
. The narrowing primitive conversion (JLS 5.1.3) is hidden in the compound assignment operation.
JLS 15.26.2 Compound Assignment Operators (JLS Third Edition):
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.
For example, the following code is correct:
short x = 3; x += 4.6;
and results in
x
having the value7
because it is equivalent to:short x = 3; x = (short)(x + 4.6);
This also explains why the following code compiles:
byte b = 1;
int x = 5;
b += x; // compiles fine!
But this doesn't:
byte b = 1;
int x = 5;
b = b + x; // DOESN'T COMPILE!
You need to explicitly cast in this case:
byte b = 1;
int x = 5;
b = (byte) (b + x); // now it compiles fine!
It's worth noting that the implicit cast in compound assignments is the subject of Puzzle 9: Tweedledum from the wonderful book Java Puzzlers. Here are some excerpt from the book (slightly edited for brevity):
Many programmers think that
x += i;
is simply a shorthand forx = x + i;
. This isn't quite true: if the type of the result is wider than that of the variable, the compound assignment operator performs a silent narrowing primitive conversion.To avoid unpleasant surprises, do not use compound assignment operators on variables of type
byte
,short
, orchar
. When using compound assignment operators on variables of typeint
, ensure that the expression on the right-hand side is not of typelong
,float
, ordouble
. When using compound assignment operators on variables of typefloat
, ensure that the expression on the right-hand side is not of typedouble
. These rules are sufficient to prevent the compiler from generating dangerous narrowing casts.For language designers, it is probably a mistake for compound assignment operators to generate invisible casts; compound assignments where the variable has a narrower type than the result of the computation should probably be illegal.
The last paragraph is worth noting: C# is a lot more strict in this regard (see C# Language Specification 7.13.2 Compound assignment).