This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Autoboxing { | |
static Map<Long, String> map = new HashMap<Long, String>(); | |
public static void put(long key, String value) { | |
map.put(key, value); | |
} | |
public static void main(String[] args) { | |
put(1, "foo"); | |
put(2, "bar"); | |
map.remove(1); | |
System.out.println(map); | |
} | |
} |
It puts two values into a map, removes one, and then prints the content of the map. Simple enough, but here's the output:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{1=foo, 2=bar} |
Well that's interesting; somehow "foo" did not get removed from the map. Looking closely at the Java API for Map, we see that the remove method takes an Object as its parameter (the reasons for this are subtle and discussed in various places). So when we call remove on the map, the value 1 gets autoboxed to... an Integer. Unfortunately, the map uses Longs for keys, and the Integer 1 is not equal to the Long 1, so remove does not find the key and does nothing. But since the argument to remove has type Object, the code compiles fine and you are blissfully unaware of the impending doom until your system crashes and burns horribly (or you catch the mistake with a unit test!). Sad times. And all of this is fixed by writing "1L" (primitive long) instead of just "1" (primitive int).
This subtle interaction between the collection APIs and autoboxing should make any Java programmer very anxious. It is difficult to spot such mistakes in code, especially because you expect the compiler to catch this kind of error. The best solution I have for avoiding this problem is being very rigid about passing the correct type of primitive; if a value is an int, don't ever store it in a long, and vice versa. This can be somewhat annoying (personally, "1L" is much uglier to read than "1") but may save you some headaches in the future. Alternatively, don't use boxed primitives in collections, but that seems like an even harder standard to maintain. Regardless, add autoboxing to your list of things in Java that you just have to deal with even though the point is that you shouldn't have to think about it.
No comments:
Post a Comment