Java Compound Assignment Operators: What Could Go Wrong?

In my last post about arithmetic operators, I mentioned following up with a post about when the compound assignment operators don’t work as expected.  The idea came to me because I was using the *= operator to multiply numbers in a loop and I was not getting the numbers I was expecting to get.  That got me looking into whether it was possible for the compound operators to not work correctly.  In the end, the reason for my problem was rather mundane: I wasn’t getting the answers I expected because I wasn’t starting with the numbers I thought I was.

Before we get started, let’s quickly review what a compound assignment operator is: a shorthand way to operate on a variable and assign the value back to itself.

int i = 1;
i += 1;

Unexpected Results

Let’s define a byte and perform some operations on it:

byte b = 0;
b += 1;

When we print out the value of b it’s equal to 1 just as we expect.

Let’s go a little bigger now:

b += 200;

When we print out the value of b we get… -55. We were expecting 201 right?

If we were to try the following, it wouldn’t even compile:

b = b + 200;

It turns out that according to the Java language specification for compound assignment operators that the casting is done automatically.

So b += 200 is actually equivalent to b = (byte) (b + 200) which as we’ve seen gives us weird results. Since bytes can only hold values between -128 and 127, we see these strange results.

For this example, I chose to use byte to keep the numbers small, but you could see similar issues with short and int if you’re working with large enough numbers.

In fact, it doesn’t take much to run into trouble with short either, especially if you’re multiplying:

short s = 5000;
s *= 5000;
System.out.println(s);

The example above gives us 30,784 instead of the 25,000,000 we were expecting.

Conclusion

The take away here is that we should be careful about the data types we choose when we’re using compound operators.  The implicit casting that Java does when using them, means that the compiler isn’t going to save us.  This is especially true when we’re using the *= and /= operators that can yield larger results more quickly.

References/further reading: