public class Npe { static class Thing { long value; } public static Map<Thing, Long> map; public static void main(String[] args) { Thing thing = new Thing(); method(null); // returns -1 method(thing); // returns 0 map = new HashMap<Thing, Long>(); method(null); // returns -1 method(thing); // NullPointerException thrown inside this method call } public static long method(Thing thing) { if (thing == null) { return -1; } Long v = (map == null) ? thing.value : map.get(thing); // NPE here if (v == null) { v = thing.value; } return v; } }
On the 4th call to method()
I get a NullPointerException
thrown on the indicated line inside method()
. If I refactor that line from
Long v = (map == null) ? thing.value : map.get(thing);
to
Long v; if (map == null) { v = thing.value; } else { v = map.get(thing); }
I get no NullPointerException
and the method behaves as it should. The question is: WHY??
It looks to me like the compiler expects the result of the ?
operator to be long
so that it is automatically unboxing (demoting from Long
to long
) the result of the call to map.get(thing)
(which may return null
and therefore throw a NullPointerException
). IMHO it should be expecting the result of the ?
operator to be Long
and autoboxing (promoting long
to Long
) thing.value
instead.
Even better, if I refactor this statement:
Long v = (map == null) ? thing.value : map.get(thing);
to this (casting long
to Long
explicitly):
Long v = (map == null) ? (Long)thing.value : map.get(thing);
my IDE (IntelliJ) says that the cast is redundant, but the compiled code works as expected and does not throw a NullPointerException
! :-D
Consider your conditional expression:
(map == null) ? thing.value : map.get(thing)
The result of that expression will be long
, because the type of thing.value
is long
. See JLS §15.25 - Conditional Operator. The table in JLS 8 is a great addition. It clarifies all the possible output types for different input types. So much was the confusion related to the type of conditional expressions.
Now when you invoke this method as:
method(thing);
The map
is not null
, so the condition map == null
in your expression evaluates to false
, and then it evaluates map.get(thing)
to get the result.
Since there is no entry in map
yet, map.get(thing)
will return null
. But since the type of result is long
, an unboxing operation is performed on null
, which results in NPE
.
Now when you explicitly cast thing.value
to Long
, the type of expression becomes Long
. So no unboxing is performed on the result of map.get(thing)
, and null
is assigned to Long v
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With