Tomer Gabel's annoying spot on the 'net RSS 2.0
# Sunday, January 06, 2008

The Java implementation for generics is radically different from the C# equivalent; I won't reiterate issues that have been thoroughly discussed before, but suffice to say that Java generics are implemented as a backwards-compatible compiler extension that works on unmodified VMs.

The implications of this are considerable, and I'd like to present one of them. Lets fast forward a bit and consider a relatively new language feature in Java (introduced, I believe, with J2SE 5.0): autoboxing. A thoroughly overdue language feature, autoboxing allows the seamless transition from regular value types (e.g. the ubiquitous int) to object references (Integer); before autoboxing you couldn't simply add a value to an untyped ArrayList, you had to box (wrap) it in a reference type:

ArrayList list = new ArrayList();
list.add( 3 );			// Compile-time error
list.add( new Integer( 3 ) );	// OK

Eventually Java caught up with C# (which introduced autoboxing in 2002), and with a modern compiler the above code would be valid.

With the introductions out of the way, here's a pop-quiz: what does the following code print?

HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
map.put( 4, 2 );
short n = 4;
System.out.println( Integer.toString( map.get( n ) ) );

As a long-time C# programmer I was completely befuddled when the code resulted in a NullPointerException. Huh? Exception? What? Why?

It took me a while to figure it out: because Java generics are compile-time constructs and are not directly supported by the VM, what actually happens is that the underlying container class accepts regular Object instances (reference types); the compile-time check merely asserts that n can be promoted from short to int, whereas the actual object passed to the container class (via autoboxing) is a Short! Since the container doesn't doesn't actually have a runtime generic type per se, the collection merely looks up the reference object in the map, fails to find it (I guess the Object.hashCode implementation for value types simply returns the reference value as the hash code as in C#) and returns null. Doh! *slaps forehead*

Sunday, January 06, 2008 7:15:54 PM (Jerusalem Standard Time, UTC+02:00)  #    Comments [7] -
Development | Java
Monday, March 24, 2008 7:15:12 AM (Jerusalem Standard Time, UTC+02:00)
Way to go coming up with a contrived piece of code that wouldn't ever appear. Are you surprised that no one reads your blog?
Tuesday, June 17, 2008 10:30:19 AM (Jerusalem Standard Time, UTC+02:00)
Meh, missed this comment for a few months. What's so contrived about this code, even ignoring the fact that this actually happened to me in production code?
Tuesday, December 02, 2008 2:34:35 AM (Jerusalem Standard Time, UTC+02:00)
The compile time check would fail if the get() method did use the generic type.
http://java.sun.com/javase/6/docs/api/java/util/Map.html#get(java.lang.Object)

As you can see, get takes an Object parameter. So when the compiler boxes and passes a Short it's perfectly valid, the "error" is in the API.
Dennis
Tuesday, December 02, 2008 11:24:22 AM (Jerusalem Standard Time, UTC+02:00)
Aye, that's true. I vaguely recall reading that this is one of the "weird" instances where they had to revert to Objects for backwards compatibility reasons.

Sunday, August 02, 2009 12:23:48 AM (Jerusalem Standard Time, UTC+02:00)

Sorry, this is an ancient post. I don't know if anyone is still listening for updates...

But do you know of a tool that would flag autoboxing and unboxing at build time?

I have my eclipse options set to warn me in the IDE, but not all contributors do and I'd like to generate a warning or error in the scheduled ant build if anyone does add code with autoboxing/unboxing.

Thanks,
Jonathan
Sunday, August 02, 2009 9:04:55 AM (Jerusalem Standard Time, UTC+02:00)
Jonathan: Not one I'm directly familiar with, but there are dozens of static analyzers for Java -- one of them must have that option...
Tuesday, November 17, 2009 5:26:27 PM (Jerusalem Standard Time, UTC+02:00)
Your example doesn't seem at all contrived - Hibernate regularly generates code with short integers in it.

Additionally that example applies to a much more common situation - where n is a long. Without boxing, the compiler wouldn't even allow the conversion to an int without a specific cast, but since it is converting to a Long instead, it compiles just fine.

The bigger problem with boxing is that blurs the contracts (for the parameters and returned variables) between methods and their callers: For example, returning an int is a not-null contract, whereas returning an Integer is a contract that allows null (unless specifically stated otherwise in your JavaDoc). The lack of this clear contract is what leads so often to the Null-Pointer Exceptions that occur so often when is allowed.

One final thought: I once saw this in a loop counting hundreds of iterations:

Integer sendCount;
...
for( sendCount = 0; sendCount < 500; sendCount++) {
...
}

I can find no reason for allowing in Java production code.
OpenID
Please login with either your OpenID above, or your details below.
Name
E-mail
Home page

Comment (Some html is allowed: a@href@title, b, blockquote@cite, em, i, strike, strong, sub, super, u) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Live Comment Preview
Me!
Send mail to the author(s) Be afraid.
Archive
<July 2010>
SunMonTueWedThuFriSat
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567
All Content © 2010, Tomer Gabel
Based on the Business theme for dasBlog created by Christoph De Baene (delarou)