Excessive Use of Java Autoboxing Leads to Grief
I've been hip-deep in Hemlock again this afternoon - trying to track down a NullPointerException whose reported location is giving me absolutely no help in trying to figure out the problem. I spent about an hour trying to figure out the problem until I finally stumbled on the answer and got it fixed. But the problem was an enormous pain in the rear, and related to excessive use of Java's autoboxing feature.
The problem was that the original developer of Hemlock had decided to use Double objects as instance variables as opposed to using the elemental double. This is not because he wanted to use the ability to assign null to these values - no, it's just because he was being lazy, that's the best I can figure.
If you use Double objects and then mix in arithmetic operations, you're counting on Java's autoboxing to do all the real work for you. Whereas, if you used double you might have to think about illegal values, but in this case, that wouldn't be a problem.
What I had done to the code to simplify it was to replace the use of dozens of ivars with a map. Specifically, a ConcurrentHashMap<String, Double> where I would make the setters and getters use specific keys so that the API to the class appears as if the ivars as distinct, but they're all really being looked up in the map.
This was done because the data needed to be outputted in a JSP as an XML data stream. With the Map, I was able to make a very simple loop and the dozens of getters and output statements were reduced to a few in a loop. Massive simplification that was really a long time overdue. Everything was working fine until I ran into the following code:
Double newValue = (obj == null ? 0.0 : obj.getMyValue());
where:
public Double getMyValue() { return (_values == null ? null : _values.get("myValue")); }
where the _values is the ConcurrentHashMap<String, Double> that stored all the values.
Because of the nature of the ConcurrentHashMap<String, Double> neither the key nor the value could be null. Still, I was setting the value to a Double so that shouldn't be an issue. Or should it?
When I added setting a new Double(0.0) for all values in the _values Map, the error went away. What's the lesson here? I believe it's that the conditional having the 0.0 first sets the expected datatype for the second part of the conditional, and when it's null, a NullPointerException is thrown, but since it's in the return call, the exception is actually reported on the line where the method returns.
What have I learned? That with autoboxing, you have to be very, very careful about the nulls floating around your code. But more importantly, their use should really be limited because the overhead of the conversions is hidden in the code, and problems such as these don't show up as easily.
Then again, I've never had problems with these guys, but then again, I harly ever use them. I'm far more interested in the explicit conversions than implicit conversions done by the compiler or runtime.