Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't compound assignment in Java catch overflow problems?

To my shock, it turns out that the following code will compile without even warnings:

public void test()
{
    int value = 2000000000;
    long increment = 1000000000;
    value += increment;
}

Whereas this gives a compile-time error, as you would expect:

public void test()
{
    int value = 2000000000;
    long increment = 1000000000;
    value = value + increment;
}

I checked it up and indeed, the JLS (section 15.26.2) has this to say:

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.

This seems ridiculous to me. Why did they feel the need to explicitly cast here? It seems that automatic type conversion would have handled the widening anyway, and automatically narrowing like this is pretty much guaranteed to result in integer overflow.

like image 604
Hakanai Avatar asked Mar 30 '11 01:03

Hakanai


2 Answers

Here is one explanation:

When you do an assignment (the first code snippet), java enforces type checking because the LHS and RHS may very well be independent of each other.

But the compound-operator is more like an incremental operator. The += modifies the value of the variable involved, rather than assigning a new value to the variable. When you modify a byte, you expect a byte as the result. To make life easier, java does an implicit type conversion for compound-operators because they are modifiers.

like image 164
Dave L. Avatar answered Nov 08 '22 15:11

Dave L.


The compound assignment operators are specified by the JLS (15.26.2) as follows:

"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."

In this case E1 is of type int E2 is of type long, and op is +. So this is equivalent to:

value = (int)(value + increment);

The addition of an int and a long gives a long that is then cast back to an int before assignment. That is all fine, hence no compilation error.

The difference between this and the simple assignment (i.e. value = value + increment;), is that the simple assignment doesn't have a typecast.


OK, so why did they define it this way?

I think that the reason is to make examples like this work:

    byte b = ...
    b += 1;

Without the typecast, b += 1 would be a compilation error and you'd need to write it as:

    b += (byte) 1;
like image 33
Stephen C Avatar answered Nov 08 '22 17:11

Stephen C