Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this assignment cause NPE?

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

like image 214
David Wasser Avatar asked Apr 10 '14 15:04

David Wasser


1 Answers

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.

like image 192
Rohit Jain Avatar answered Oct 13 '22 05:10

Rohit Jain