Boxing with Java

A bug was recently identified in a project I’m working on. Check the following code out:

Naughty naughty. I had done a very silly thing and used == to test equality of Integer objects! As we all know, when comparing equality on objects we should be using .equals(). However, this bug was intermittent. Comparing Integer to Integer like this works. Sometimes.

Let’s see what’s happening here:

First, we need to check what happens when we compare two ints:

Output:

Comparing unboxed 10 to 10
true

This makes sense. A is of an equal value to B.

Now let’s compare two Integer objects:

Output:

Comparing boxed 10 to 10
true

Great – we have equal values, right? Where’s the problem?

Here:

Change the values to some high number. Let’s say 1000:

Comparing boxed 1000 to 1000
false

Depending on your particular implementation of Java, the value required to trigger this behaviour may vary, but in my case, the equality result changes when a and b are set to 128.

UPDATE: As pointed out by Edwin Nathaniel in the comments, any Java implementation should cache Integer objects according to the following:

“Cache to support the object identity semantics of autoboxing for values between -128 and 127 (inclusive) as required by JLS.
The cache is initialized on first usage. The size of the cache may be controlled by the -XX:AutoBoxCacheMax= option.”

This means that the Integer cache limits are configuration dependent, not implementation dependent. Props to Edwin (who also has a rather delicious looking blog) for schooling me on this subject!

Let’s change the code a little and turn our primitives into objects when we do the comparison (this is what’s already happening, by the way, it’s just a little less obvious the way I’ve illustrated it here):

Output:

Comparing boxed 1000 to 1000
false

In case you missed it, the checkBoxed method is expecting Integer objects, not primitives. We pass it primitives, and autoboxing is applied by the compiler, cramming our primitives into objects and giving us a nasty surprise.

Autoboxing allows developers to forget about the tedium of converting between primitives and objects, resulting in tidier code and, in this particular case, more headaches. You don’t gain anything performance-wise as the compiler will still generate the ‘boxing’ code, but it does tend to make life more convenient.

If you want to avoid this problem, you can do the following:

Comparing these two using the checkBoxed method will always output ‘false’, because you are explicitly creating two different objects. The fact that they represent the same value is irrelevant to the JVM. It sees two different objects and tells you so.

You can also use the following comparison instead:

Perhaps the best way to avoid this problem is to be consistent and enforce the rule across your team. Keep in mind that you don’t always know how Bill has implemented his bit of code, and he doesn’t know how you’ve implemented yours – so decide how you’re going to deal with this potential pitfall and make sure you both keep it in mind. Unfortunately for me, I’m the only person working on this project so I can’t blame somebody else.

It’s worth pointing out that autoboxing is applied to all primitive wrapper types, not just Integers, so it is an important feature to keep in mind.

If you use Eclipse, turn on the following handy feature to help you find where autoboxing / unboxing could occur:

This will highlight your code with the following warning:

This is one of those features added to make your life easier. Unfortunately, people new to Java, or idiots like me, may not understand what is happening here and could be writing code laden with boxing issues. Only a good set of coding standards, and a decent code-review process, will help you catch these issues before they become a real problem out in the field.