It was very confusing to me to observe this situation:
Integer i = null;
String str = null;
if (i == null) { //Nothing happens
...
}
if (str == null) { //Nothing happens
}
if (i == 0) { //NullPointerException
...
}
if (str == "0") { //Nothing happens
...
}
So, as I think boxing operation is executed first (i.e. java tries to extract int value from null
) and comparison operation has lower priority that's why the exception is thrown.
The question is: why is it implemented in this way in Java? Why boxing has higher priority then comparing references? Or why didn't they implemented verification against null
before boxing?
At the moment it looks inconsistent when NullPointerException
is thrown with wrapped primitives and is not thrown with true object types.
i is an Integer and the 0 is an int so in the what really is done is something like this
And this cause the nullPointer because the i is null. For String we do not have this operation, thats why is no exception there.
The makers of Java could have defined the
==
operator to directly act upon operands of different types, in which case givenInteger I; int i;
the comparisonI==i;
could ask the question "DoesI
hold a reference to anInteger
whose value isi
?"--a question which could be answered without difficulty even whenI
is null. Unfortunately, Java does not directly check whether operands of different types are equal; instead, it checks whether the language allows the type of either operand to be converted to the type of the other and--if it does--compares the converted operand to the non-converted one. Such behavior means that for variablesx
,y
, andz
with some combinations of types, it's possible to havex==y
andy==z
butx!=z
[e.g. x=16777216f y=16777216 z=16777217]. It also means that the comparisonI==i
is translated as "Convert I to anint
and, if that doesn't throw an exception, compare it toi
."In
i == 0
Java will try to do auto-unboxing and do a numerical comparison (i.e. "is the value stored in the wrapper object referenced byi
the same as the value0
?").Since
i
isnull
the unboxing will throw aNullPointerException
.The reasoning goes like this:
The first sentence of JLS § 15.21.1 Numerical Equality Operators == and != reads like this:
Clearly
i
is convertible to a numeric type and0
is a numeric type, so the binary numeric promotion is performed on the operands.§ 5.6.2 Binary Numeric Promotion says (among other things):
§ 5.1.8 Unboxing Conversion says (among other things):
Your NPE example is equivalent to this code, thanks to autoboxing:
if ( i.intValue( ) == 0 )
Hence NPE if
i
isnull
.It's because of Javas autoboxing feature. The compiler detects, that on the right hand side of the comparison you're using a primitive integer and needs to unbox the wrapper Integer value into a primitive int value as well.
Since that's not possible (it's null as you lined out) the
NullPointerException
is thrown.The Short Answer
The key point is this:
==
between two reference types is always reference comparisonInteger
andString
, you'd want to useequals
instead==
between a reference type and a numeric primitive type is always numeric comparisonnull
always throwsNullPointerException
String
, it is in fact NOT a primitive typeThe above statements hold for any given valid Java code. With this understanding, there is no inconsistency whatsoever in the snippet you presented.
The Long Answer
Here are the relevant JLS sections:
This explains the following:
Both operands are reference types, and that's why the
==
is reference equality comparison.This also explains the following:
For
==
to be numerical equality, at least one of the operand must be a numeric type:This explains:
Here's an excerpt from Effective Java 2nd Edition, Item 49: Prefer primitives to boxed primitives:
There are places where you have no choice but to use boxed primitives, e.g. generics, but otherwise you should seriously consider if a decision to use boxed primitives is justified.
References
Integer
to typeint
"r
isnull
, unboxing conversion throws aNullPointerException
"==
and!=
==
and!=
Related questions
Integers
in Java does auto-unboxing occur?==
but notequals()
?Related questions
int num = Integer.getInteger("123")
throwNullPointerException
? (!!!)String.equals
versus==