2013-07-09 6 views
12

Quando scrivo il mio codice Java come questo:strano comportamento quando si utilizza Java operatore ternario

Map<String, Long> map = new HashMap<>() 
Long number =null; 
if(map == null) 
    number = (long) 0; 
else 
    number = map.get("non-existent key"); 

l'applicazione funziona come previsto, ma quando faccio questo:

Map<String, Long> map = new HashMap<>(); 
Long number= (map == null) ? (long)0 : map.get("non-existent key"); 

ho un NullPointerException sul seconda linea. Il puntatore di debug passa dalla seconda linea a questo metodo nella classe java.lang.Thread:

/** 
    * Dispatch an uncaught exception to the handler. This method is 
    * intended to be called only by the JVM. 
    */ 
    private void dispatchUncaughtException(Throwable e) { 
     getUncaughtExceptionHandler().uncaughtException(this, e); 
    } 

Cosa sta succedendo qui? Entrambi questi percorsi di codice sono esattamente equivalenti, vero?


Modifica

Sto usando Java 1.7 U25

+0

Perché stai provando per l'impossibile? Crei tu stesso la 'mappa '; se è 'null', il tuo sistema runtime è rotto. Dato che questa è ovviamente una versione abbreviata di altri lavori che stai facendo, ti suggerisco di assicurarti che gli oggetti siano sempre inizializzati, quindi non devi fare i controlli 'map == null'. Le chiavi sono il tuo problema. –

+0

@Eric Jablow Questa è solo una versione semplificata del mio codice. La mappa in questione viene effettivamente recuperata da un altro luogo in cui vengono creati dinamicamente su richiesta. Ecco perché il controllo. Qui ho incluso l'inizializzazione in modo che sia chiaro alle persone che stanno rispondendo che il caso if-true non viene eseguito quando il mio problema si verifica – radiantRazor

+0

Ora capisco. Probabilmente la cosa migliore sarebbe lanciare un'eccezione immediatamente se qualcuno dovesse passare una mappa 'nullo' al tuo codice. Oppure, se qualcuno passa in un 'nullo' al tuo codice, imposta la tua mappa su uno vuoto. –

risposta

15

Non sono equivalenti.

Il tipo di questa espressione

(map == null) ? (long)0 : map.get("non-existent key"); 

è long perché il vero risultato è di tipo long.

La ragione questa espressione è di tipo long è dalla sezione §15.25 of the JLS:

Se una delle seconde e terze operandi è di tipo primitivo T, e il tipo di l'altro è il risultato dell'applicazione di conversione boxe (§5.1.7) a T, quindi il tipo di espressione condizionale è T.

Quando si ricerca una chiave inesistente i rendimenti mapnull. Quindi, Java sta tentando di cancellarlo in un long. Ma è null. Quindi non può e ottieni uno NullPointerException. Puoi risolvere il problema dicendo:

Long number = (map == null) ? (Long)0L : map.get("non-existent key"); 

e quindi starai bene.

Tuttavia, qui,

if(map == null) 
    number = (long) 0; 
else 
    number = map.get("non-existent key"); 

dal number è dichiarato come Long, che unboxing ad un long non si verifica mai.

+0

In tal caso, il poster può utilizzare l'operatore ternario convertendo 0 in 'Long' invece di' long'? – MathSquared

+1

Sì, avrebbe dovuto assegnare '0L' a' Long'. – jason

+2

Woww, il JLS ha così tante catture nascoste. La mia domanda di follow up ovvia sarebbe perché il JLS è definito per inferire il tipo come primitivo? Se è stato definito che il tipo deve essere dedotto come il tipo autoboxed, allora tali eccezioni di nullpointer potrebbero essere state evitate. Proverò a chiedermelo come una domanda separata forse – radiantRazor

3

Cosa sta succedendo qui? Entrambi questi percorsi di codice sono esattamente equivalenti, vero?

non sono equivalenti; l'operatore ternario ha alcuni avvertimenti.

L'argomento se vero del operatore ternario, (long) 0, è del tipo primitivo long. Di conseguenza, l'argomento se-false sarà automaticamente unboxed da Long a long (secondo JLS §15.25):

Se una delle seconde e terze operandi è di tipo primitivo T, e il tipo di l'altro è il risultato dell'applicazione della conversione di boxe (§5.1.7) a T, il tipo di espressione condizionale è T.

Tuttavia, questo argomento è null (dal momento che la tua mappa non contiene la stringa "non-existent key", che significa get() rendimenti null), quindi un NullPointerException si verifica durante il processo di unboxing.

+0

Ma perché il tipo del valore if-true influisce sul tipo di valore if-false? Non dovrebbe essere determinato dal tipo sulla LHS? – radiantRazor

+1

@radiantRazor In breve, perché è così che il JLS definisce l'operatore ternario. – arshajii

0

Ho commentato sopra suggerendo che si assicura che map non sia mai null, ma questo non aiuta il problema ternario. In pratica, è più facile lasciare che il sistema esegua il lavoro per te. Poteva usare Apache Commons Collections 4 e la sua classe DefaultedMap.

import static org.apache.commons.collections4.map.DefaultedMap.defaultedMap; 

Map<String, Long> map = ...; // Ensure not null. 
Map<String, Long> dMap = defaultedMap(map, 0L); 

Google Guava non ha nulla facile come questo, ma si può avvolgere map con il metodo Maps.transformValues().