I have this code:
package tests;
import java.util.Hashtable;
public class Tests {
public static void main(String[] args) {
Hashtable<String, Boolean> modifiedItems = new Hashtable<String, Boolean>();
System.out.println("TEST 1");
System.out.println(modifiedItems.get("item1")); // Prints null
System.out.println("TEST 2");
System.out.println(modifiedItems.get("item1") == null); // Prints true
System.out.println("TEST 3");
System.out.println(Boolean.valueOf(null)); // Prints false
System.out.println("TEST 4");
System.out.println(Boolean.valueOf(modifiedItems.get("item1"))); // Produces NullPointerException
System.out.println("FINISHED!"); // Never executed
}
}
My problem is that I don't understand why Test 3 works fine (it prints false
and doesn't produce NullPointerException
) meanwhile Test 4 throws a NullPointerException
. As you can see in tests 1 and 2, null
and modifiedItems.get("item1")
are equals and null
.
The behavior is the same in Java 7 and 8.
You've got to look carefully at which overload is being invoked:
Boolean.valueOf(null)
is invokingBoolean.valueOf(String)
. This doesn't throw anNPE
even if supplied with a null parameter.Boolean.valueOf(modifiedItems.get("item1"))
is invokingBoolean.valueOf(boolean)
, becausemodifiedItems
's values are of typeBoolean
, which requires an unboxing conversion. SincemodifiedItems.get("item1")
isnull
, it is the unboxing of that value - not theBoolean.valueOf(...)
- which throws the NPE.The rules for determining which overload is invoked are pretty hairy, but they roughly go like this:
In a first pass, a method match is searched for without allowing boxing/unboxing (nor variable arity methods).
null
is an acceptable value for aString
but notboolean
,Boolean.valueOf(null)
is matched toBoolean.valueOf(String)
in this pass;Boolean
isn't an acceptable for eitherBoolean.valueOf(String)
orBoolean.valueOf(boolean)
, so no method is matched in this pass forBoolean.valueOf(modifiedItems.get("item1"))
.In a second pass, a method match is searched for, allowing boxing/unboxing (but still not variable arity methods).
Boolean
can be unboxed toboolean
, soBoolean.valueOf(boolean)
is matched forBoolean.valueOf(modifiedItems.get("item1"))
in this pass; but an unboxing conversion has to be inserted by the compiler to invoke it:Boolean.valueOf(modifiedItems.get("item1").booleanValue())
(There's a third pass allowing for variable arity methods, but that's not relevant here, as the first two passes matched these cases)
Method signature
The method
Boolean.valueOf(...)
has two signatures:public static Boolean valueOf(boolean b)
public static Boolean valueOf(String s)
Your
modifiedItems
value isBoolean
. You cannot castBoolean
toString
so consequently the first signature will be chosenBoolean unboxing
In your statement
which can be read as
However,
modifiedItems.get("item1")
returnsnull
so you'll basically havewhich obviously leads to a
NullPointerException
A way to understand it is when
Boolean.valueOf(null)
is invoked, java is precisely being told to evaluate null.However, when
Boolean.valueOf(modifiedItems.get("item1"))
is invoked, java is told to obtain a value from the HashTable of object type Boolean, but it doesn't find the type Boolean it finds a dead end instead (null) even though it expected Boolean. The NullPointerException exception is thrown because the creators of this part of java decided this situation is an instance of something in the program going wrong that needs the programmer's attention. (Something unintended happened.)In this case it is more the difference between deliberately declaring that you intended the null to be there, and java finding a missing reference to an object (null) where an object was intended to be found.
See more information about NullPointerException in this answer: https://stackoverflow.com/a/25721181/4425643
As Andy already very well described the reason of
NullPointerException
:which is due to Boolean un-boxing:
get converted into:
at runtime and then it throw
NullPointerException
ifmodifiedItems.get("item1")
is null.Now I would like to add one more point here that un-boxing of the following classes to their respective primitives can also produce
NullPointerException
exception if their corresponding returned objects are null.Here is the code:
Since
modifiedItems.get
returns aBoolean
(which is not castable to aString
), the signature that would be used isBoolean.valueOf(boolean)
, where theBoolean
is outboxed to a primitiveboolean
. Oncenull
is returned there, the outboxing fails with aNullPointerException
.