2008-05-04

A snack-sized bite of Java for tonight

With Java autoboxing, always pay attention to what your primitives get boxed into.

Here's an easy little bug to introduce if you don't pay attention: consider these methods of the Map<K, V> interface:

V put(K key, V value)
V remove(Object key)

So you put a value into a map, and remove it. Easy enough.

Pop Quiz

So what does the following return?

public static String test() {
HashMap<Long, String> map = new HashMap<Long, String>();
final long some_key = 1;
map.put(some_key, "foo");
return map.remove(1);
}


...



...



...



...



Answer: null.



This is why it's important to remember the definition of the put, get and remove methods. While the generified [nda - yes that's a word, according to the Sun docs] flavour of put(K, V) ensures the key is an instance of K, for whatever K you declared for this generic map, the get and remove methods weren't generified and simply take an Object. This can bite you, as in the above function, where autoboxing essentially caused the following calls to happen, though it probably wasn't the programmer's intent:



    map.put(new Long(1), "foo");
map.remove(new Integer(1));


Even though Integer(1) and Long(1) have the same hashCode() value, they fail the equals() test, so key lookups when using the wrong wrapper class will always fail. The fix, of course, is to declare your keys as the right type when calling get() or remove(), and be careful, as the method signature will allow anything to be passed in, so you'll never know at compile-time you got it wrong.



 



Happy coding!



1 comment:

Fortrel said...

Hmmm. Interesting. Now that you explain it, it makes perfect sense! Another good reason not to use auto-boxing for primitive. Oh well...