2014-04-10 21 views
25
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; 
    } 
} 

Al 4 ° chiamata a method() ottengo un NullPointerException gettato sulla linea indicata all'interno method(). Se ho refactoring che la linea daPerché questo incarico causa NPE?

Long v = (map == null) ? thing.value : map.get(thing); 

a

Long v; 
if (map == null) { 
    v = thing.value; 
} else { 
    v = map.get(thing); 
} 

non ottengo alcuna NullPointerException e il metodo si comporta come dovrebbe. La domanda è: PERCHÉ ??

Sembra a me come il compilatore si aspetta il risultato dell'operatore ? per essere long in modo che sia unboxing automaticamente (l'abbassamento di livello da Long al long) il risultato della chiamata a map.get(thing) (che può tornare null e quindi lanciare un NullPointerException). IMHO dovrebbe aspettarsi che il risultato dell'operatore ? sia Long e autoboxing (promozione di long a Long) thing.value invece.

Ancora meglio, se mi refactoring questa dichiarazione:

Long v = (map == null) ? thing.value : map.get(thing); 

a questo (casting long a Long esplicitamente):

Long v = (map == null) ? (Long)thing.value : map.get(thing); 

mio IDE (IntelliJ) dice che il cast è ridondante, ma il codice compilato funziona come previsto e non genera uno NullPointerException! :-D

+2

possibile duplicato di [Booleani, operatori condizionali e autoboxing] (http://stackoverflow.com/questions/3882095/booleans-conditional-operators-and-autoboxing) –

+10

@DwB Il tuo commento non è rilevante in quanto non sono un programmatore junior. E il tuo suggerimento di "non usare l'operatore ternario" è anche stupido. Solo perché qualcosa può essere complicato o confuso che non significa che non dovresti usarlo. Significa solo che devi stare attento e sapere cosa stai facendo. Se gli sviluppatori non avessero mai usato qualcosa di complicato o potenzialmente confuso, sarebbero stati tutti pittori o spazzini o giardinieri al posto dei programmatori ;-) –

+1

Se non si capisce un operatore ternario, allora, che ci crediate o no, siete ancora junior con Giava. Inoltre, il fatto che qualcosa sia complicato, confuso e privo di valore è una buona ragione per non usarlo. Se gli sviluppatori non avessero mai usato qualcosa di complicato e potenzialmente confuso, avrebbero prodotto un codice meno complicato e confuso. – DwB

risposta

28

Considerate la vostra espressione condizionale:

(map == null) ? thing.value : map.get(thing) 

Il risultato di questa espressione sarà long, perché il tipo di thing.value è long. Vedi JLS §15.25 - Conditional Operator. Il tavolo di JLS 8 è un'ottima aggiunta. Chiarifica tutti i possibili tipi di output per diversi tipi di input. Tanto era la confusione relativa al tipo di espressioni condizionali.

Ora, quando si richiama questo metodo come:

method(thing); 

Il map non è null, quindi la condizione map == null nella vostra espressione restituisce false, e poi si valuta map.get(thing) per ottenere il risultato.

Poiché non è presente alcuna voce in map, map.get(thing) restituirà null. Ma poiché il tipo di risultato è long, viene eseguita un'operazione di annullamento della posta su null, che risulta in NPE.


Ora, quando si esegue il cast in modo esplicito thing.value al Long, il tipo di espressione diventa Long. Pertanto, non viene eseguita alcuna separazione dal risultato di map.get(thing) e null viene assegnato a Long v.

+4

* Comune * Gotcha Java :) –

+0

Perché dici che il risultato dell'espressione sarà 'long'? Direi che il risultato dell'espressione è 'Long', perché il terzo argomento è' Long'. –

+1

@DavidWasser Il risultato dell'espressione condizionale dipende dal tipo di 2 ° e 3 ° operando. Non solo sull'operando che sarà la risposta. Vedi la tabella nel link JLS. Se uno qualsiasi degli operandi è di tipo primitivo, il risultato sarà di tipo primitivo. –

0

Questa è la mia comprensione di ciò che sta accadendo:

quando si utilizza Long v = (map == null) ? thing.value : map.get(thing); // NPE here

il map.get(thing) restituisce una Long che è null e poi c'è un tentativo di unboxing il suo valore a long (perché il tipo di espressione se lungo) - questo causa NPE.

Tuttavia, quando si utilizza il modulo lungo, si evita accuratamente l'operazione di annullamento dell'annullamento su Null lungo.

+0

ok Sto usando una versione di IE che non crede di mostrare gli aggiornamenti SO così rapidamente come accadono, e non entro 22 minuti almeno :) – Bhaskar

Problemi correlati