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.

15 Replies to “Boxing with Java”

  1. Tys for sharing the autoboxing feature.
    I have not looked at specs, i am guessing Integer is immutable. Thus, it is okay for compiler to optimize by sharing the same reference when an integer literal is used for assignment such as Integer i = 10, Integer x = 10.

    Also, can you please share what word press plugin are you using to display images?

    1. Hi Maninder,

      You’re absolutely correct. The JVM will cache integer references up to a certain point. The spec leaves this as undefined, so you cannot rely 100% that this cache will ‘fail’ at +-128. It depends on your implementation of Java, which is yet another reason why this problem can cause major headaches!

      The plugin I’m using for image display is called Easy Fancybox: http://wordpress.org/extend/plugins/easy-fancybox/

    1. Thanks for that, Edwin. Like I said in my post – this is an implementation thing and so YMMV depending on whichever implementation of Java you’re using, but it’s good to see the actual source code behind this!

  2. Semi-nitpick – when using Java, you want to use Integer.valueOf instead of the Integer constructor. As Edwin noted, the Integer objects in the eight-bit signed range are cached, but cannot be cached if you use the Integer constructor.

    1. I agree with you – if an implementation doesn’t follow the JLS, then it’s not an implementation – but the fact is that any ‘attempted’ implementation could have different behaviour here (bugs, simple oversight, or deliberate deviation), so people should be aware of their specific implementation’s behaviour around integer caching regardless of whether their implementation follows the JLS 100%. It’s entirely possible that somebody implements the JLS incorrectly, for example.

  3. I disagree with your last paragraph, you’re most definitely not an idiot, I would rather reserve this title to those who decided that autoboxing/unboxing was a good idea and added this unfortunate misfeature to Java.

  4. Although we’re talking about Java (and most projects can’t change that), it’s good to think about what different languages sometimes do to avoid surprising behavior like that. (My vote: the distinction between primitives and objects causes many hiccups, and “==” should be default call the equals-method, and there should be a “sameRefAs” method to do what Java’s “==” does.)

Leave a Reply