The Short Answer
The key point is this:
==
between two reference types is always reference comparison
- More often than not, e.g. with
Integer
and String
, you'd want to use equals
instead
==
between a reference type and a numeric primitive type is always numeric comparison
- The reference type will be subjected to unboxing conversion
- Unboxing
null
always throws NullPointerException
- While Java has many special treatments for
String
, it is in fact NOT a primitive type
The 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:
If the operands of an equality operator are both of either reference type or the null type, then the operation is object equality.
This explains the following:
Integer i = null;
String str = null;
if (i == null) { // Nothing happens
}
if (str == null) { // Nothing happens
}
if (str == "0") { // Nothing happens
}
Both operands are reference types, and that's why the ==
is reference equality comparison.
This also explains the following:
System.out.println(new Integer(0) == new Integer(0)); // "false"
System.out.println("X" == "x".toUpperCase()); // "false"
For ==
to be numerical equality, at least one of the operand must be a numeric type:
If the operands of an equality operator are both of numeric type, or one is of numeric type and the other is convertible to numeric type, binary numeric promotion is performed on the operands. If the promoted type of the operands is int
or long
, then an integer equality test is performed; if the promoted type is float or
double`, then a floating-point equality test is performed.
Note that binary numeric promotion performs value set conversion and unboxing conversion.
This explains:
Integer i = null;
if (i == 0) { //NullPointerException
}
Here's an excerpt from Effective Java 2nd Edition, Item 49: Prefer primitives to boxed primitives:
In summary, use primitives in preference to boxed primitive whenever you have the choice. Primitive types are simpler and faster. If you must use boxed primitives, be careful! Autoboxing reduces the verbosity, but not the danger, of using boxed primitives. When your program compares two boxed primitives with the ==
operator, it does an identity comparison, which is almost certainly not what you want. When your program does mixed-type computations involving boxed and unboxed primitives, it does unboxing, and when your program does unboxing, it can throw NullPointerException
. Finally, when your program boxes primitive values, it can result in costly and unnecessary object creations.
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
Related questions
Related questions