Eclipse gave a surprising left jab while unboxing

I'm working on a project for a client to integrate an existing web-based mortgage application to work with a large mortgage-loan consolidator. The existing application has a large code base originally targeted for Java 1.3. We needed to create an integration API and wanted to take advantage of some of the concurrency classes introduced in Java 1.5. The client gave approval to use 1.5 for the new code.

Upgrading went smoothly. We had to rename an existing package that included the new enum keyword, but the 1.3 code easily upgraded to 1.5. Being able to use 1.5 was nice because I like the generics support, the simplified "for-each" syntax to iterate over collections, and the simplified concurrent package. Many of the new concurrency features also are available for earlier versions of Java in the backport-util-concurrent library. I like Eclipse's support for 1.5, such as typing "foreach[Ctrl-SPACE]" and having Eclipse make a pretty good guess at filling in the simplified for-each loop code, including the collection reference to iterate over, and picking a good name for the temporary iterator variable.

For example, if you start a method:
public CreditReport getTriMergeForBorrower(
Borrower borrower, Set creditReports
) {
and you want to loop over the creditReports to search for the correct one for this borrower, you can type:
foreach[Ctrl-SPACE]
and Eclipse will replace that with:
for (CreditReport report : creditReports) {
}
Eclipse looks "up" in the code to find the nearest iterable, fills in the correct type for the iteration temporary variable, and gives the temporary variable a reasonable name. Pretty nice. But as the title of this entry implies, I got an unexpected jab from Eclipse, or more accurately, my reliance on it.

Eclipse is aware of another "new" feature in 1.5: autoboxing and unboxing. Autoboxing is the term for the Java compiler allowing you to write code that treats primitives as their equivalent object types (an int treated as if it were a java.lang.Integer, for example). Auto-unboxing is the opposite: using an object when the expression calls for a primitive. If you haven't been able to use Java 1.5 on a project, here's a short introduction.

With autoboxing, you can write code like this (from the above-reference Sun website):
public static void main(String[] args) {
Map m = new TreeMap();
for (String word : args) {
Integer freq = m.get(word);
m.put(word, (freq == null ? 1 : freq + 1));
}
System.out.println(m);
}
Notice how it appears you can pass an int as the parameter to be inserted into the TreeMap. Even though you really can't put a primitive into a collection like a map, the compiler (not the Java runtime) corrects this "incorrect" coding by inserting hidden code to create an Integer object to wrap the primitive int value. Autoboxing makes the code look a little cleaner: Let the compiler do the work rather than the programmer.

I've known you could write autoboxing code like the above for a couple of years. In August, 2004, two months before Java 1.5's general release, I attended a talk by Joshua Bloch and Neal Gafter at the Denver Java Users Group, introducing the new features in Java 1.5 (Tiger). But what I hadn't considered was that you sometimes could write auto-unboxing code without realizing it. It happened to me with code like:
if (creditReport != null && creditReport.isTriMerge()) {
// do some processing
}
When testing the code, it threw a NullPointerException. When I saw the stack trace, I said Huh? The creditReport reference obviously isn't null when invoking isTriMerge, so where did the NPE come from? A moment later, it hit me. The isTriMerge method must be returning a Boolean, not a boolean as I had assumed when I looked at the business object's API using Eclipse's Ctrl-SPACE to show options. I let Eclipse fill in the isTriMerge method name when I typed the if statement. Eclipse didn't complain about the syntax, so my natural assumption was a method named "isXXX" would return a boolean.

Instead, the CreditReport business object uses object wrappers for all its primitive-value fields, so when the object is persisted in the database, it can use NULL values to indicate "unspecified." The getters all return objects.

When writing code that takes advantage of unboxing, you can end up creating seemingly odd statements like:
if (creditReport.isTriMerge() != null &&
creditReport.isTriMerge()
) {
// Do something
}
where you check a value for null and whether the value is true. It looks unusual, compared to the pre-Java 1.5 version:
if (creditReport.isTriMerge() != null &&
creditReport.isTriMerge().booleanValue
) {
....
which is what the compiler is actually inserting into the .class file's bytecode.

Unexpected NullPointerExceptions occur when you don't realize that your "primitive" value is really an object being dereferenced inside hidden code. I found this big discussion of automatic unboxing on TheServerSide from three years ago, debating whether unboxing is a good thing.

So, live and learn. I don't see autoboxing/unboxing as bad. Programmers just need to be cognizant of whether a variable holds a reference to a primitive object wrapper rather than a true primitive value, and whether a method returns an object rather than a primitive. And if you want to be cautious, you can ask Eclipse to warn you about possible unboxing problems. Eclipse (I'm using 3.2) has a Java code setting: Window | Preferences | Java | Compiler | Errors/Warnings | Potential programming problems | Boxing and unboxing conversions. You can tell Eclipse to flag boxing/unboxing in the code as a warning or error. That way, you're less likely to receive an unexpected jab from hidden unboxing code. Eclipse's default is to ignore boxing and unboxing in the code as long as it's legal syntax. (I'm sure IDEA and NetBeans can do the same thing. If you're an IDEA or NetBeans user and want to post those IDE equivalents in a comment, please do.)

2 thoughts on “Eclipse gave a surprising left jab while unboxing

  1. Rather than having my problems list cluttered with autoboxing warnings, I set the syntax colouring to highlight all autoboxing in red.

  2. The solution for that kind of unboxing is making “methods that can return a null value”‘s signatures to return the wrapper version. A Boolean, and all these which only return not-null values, return the primitive.
    And, of course, when in doubt compare such as:
    if(Boolean.TRUE.equals(creditReport.isTriMerge)){
    //[…]
    }
    IMHO these “daangerous” features rely on developers’ self organization, it would be a good idea enabling IDEs to “disable” them while coding in order to make these who do not like them happy.

Comments are closed.