JDK-6918669 : Unboxing of null Values needs unique exception. Not obvious to debug otherwise.
  • Type: Enhancement
  • Component: specification
  • Sub-Component: language
  • Affected Version: 7
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2010-01-21
  • Updated: 2011-02-16
  • Resolved: 2010-06-11
Related Reports
Duplicate :  
Description
A DESCRIPTION OF THE REQUEST :
When java attempts to unbox a null value, it produces a NullPointerException, which is correct, but not as useful as it could be. It would be far more useful to return a new exception titled something like UnboxingNullValueException or UnboxingException.

JUSTIFICATION :
1) Prior to the introduction of boxing/unboxing, the only way to get an NPE was to use the dot operator, as in s.toString().length(). Now it is possible to get one through an implicit operation. Since these are fairly rare exceptions, most developers would first look at every use of the dot operator to see which one was call from a null object. Only after that fails would we then consider other possibilities

2) Unboxing is an implicit operation, so if we look at the code, trying to find out what's wrong with it, we're not likely to spot it, because that's not where the problem lies.

3) Everywhere else we see a NullPointerException, we look at the places where we used the dot operator. If this exception requires a different debugging strategy, it should have a different name.

This is especially confusing for beginners, although an experienced developer like myself gets caught by this.

In the submitted code, three lines are marked with "// np". These are lines that may throw a NullPointerException due to unboxing.

At np 1, the dot operator isn't used. (This is not very common)
At np 2, it appears that bug is the null value.
At np 3, the most common case, the dot operator is used more than once, so I start checking the values of variables that have nothing to do with the problem. (The last time I saw this problem, the line of code used the dot 49 times. I was chaining my calls, as in StringBuffer.append(). A more descriptive error message would have been far more helpful.)

If all of these instead threw an UnboxingNullValueException (which could extend NullPointerException), it would point me straight to the problem instead of leading me astray.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
When attempting to unbox a value that is equal to null, I would like to see an exception thrown that is unique to the unboxing operation, and that says so in its name.
ACTUAL -
I get a "NullPointerException," which, while it's correct, isn't very useful and is surprisingly misleading.



---------- BEGIN SOURCE ----------
public class UnBoxingBug {
	private Short size = null;

	public static void main(String[] args) {
		execute();
	}

	private Short getSize() { return size; }

	private int getArea() {
		return getSize()*getSize(); // np 1
	}

	private static void execute() {
		UnBoxingBug bug = new UnBoxingBug();
		int x = bug.getSize(); // np 2
		int y = bug.getArea();
		int z = bug.toString().length() * bug.getSize(); // np 3
	}
}
---------- END SOURCE ----------

Comments
EVALUATION A compiler for the Java programming language is in fact free to generate an instance of a subtype of NullPointerException during unboxing conversion, because a language-level type test will see the instance as a NullPointerException as promised by 5.1.8. But this RFE goes further, and mandates use of a subtype of NullPointerException in 5.1.8. This is not entirely unreasonable. NPEs from implicit unboxing conversion can be hard to track down. But I can imagine use cases where there's a lot of unboxing conversion going on, and to really help debugging, you want the subtype to represent the context (MethodInvocationUnboxException v. AssignmentUnboxException) or the reference type (BooleanUnboxException v. IntegerUnboxException) or both. On balance, and given the performance concerns expressed in other comments, it seems acceptable to mandate only NPE. Frankly, if you have dozens of method invocations in a single statement, there are so many things that could go wrong that I don't feel unboxing needs special attention. As an aside, mandating a new subtype is like "deckchairs on the Titanic" to people troubled by the rise of implicit conversions throughout the Java language. But since we're stuck with such conversions, let's note that ultimately the problem is code that thinks it's OK to use null rather than a domain-specific value when initializing a numeric reference type. (private Short size = null;) That's bad practice. Maybe the language should never have allowed assignment of the null literal to numeric reference types, but of course, that restriction is insufficient, and non-null types (6207924) should be investigated.
11-06-2010

PUBLIC COMMENTS The (spatial) overhead could be minimized by generating and using an "unbox" method, as in unbox(getSize()).valueOf where <T> T unbox(T obj) { if (obj == null) throw new UnboxingNPE(); return obj; } Again, the issue comes down to the overhead involved.
11-06-2010

PUBLIC COMMENTS I don't believe the spec intends to be quite so constraining. I don't see why a subclass of NPE could not be thrown where NPE is specified; I believe we have other cases where the spec (perhaps API more than language) mentions IndexOutOfBoundsException and we throw subclasses thereof. That said I don't see how it is feasible in this case without generating explicit null checks in the bytecode. getSize() becomes getSize().valueOf() and the runtime does not know that this was an unboxing conversion so could not specialize the exception thrown. So I believe we'd have to generate the equivalent of: Short temp = getSize(); if (temp == null) throw new UnboxingNPE()) ... which would likely be far too much overhead. Perhaps this could be referred to the Project Coin folk for their consideration and opinion.
11-06-2010

EVALUATION JLS 5.1.8 requires that a NullPointerException is thrown. We would need a spec change before we can change javac.
11-06-2010