2012-10-06 10 views
11

Sono inciampato in uno strano NullPointerException l'altro giorno causato da un cast di tipo imprevisto nell'operatore ternario. Data questa funzione (inutile esemplare):NullPointerException attraverso il comportamento di auto-boxing dell'operatore ternario Java

Integer getNumber() { 
    return null; 
} 

mi aspettavo seguenti due segmenti di codice di essere esattamente identica dopo la compilazione:

Integer number; 
if (condition) { 
    number = getNumber(); 
} else { 
    number = 0; 
} 

vs.

Integer number = (condition) ? getNumber() : 0; 

.

risulta, se condition è true, il if -affermazione funziona bene, mentre l'opration ternario nel secondo segmento di codice lancia un NullPointerException. Sembra che l'operazione ternaria abbia deciso di digitare entrambe le scelte su int prima di reinserire automaticamente il risultato in un Integer!?! Infatti, se lancio espressamente lo 0 su Integer, l'eccezione scompare. In altre parole:

Integer number = (condition) ? getNumber() : 0; 

non è lo stesso:

Integer number = (condition) ? getNumber() : (Integer) 0; 

.

Quindi, sembra che ci sia una differenza di byte code tra l'operatore ternario e un equivalente if-else -statement (qualcosa che non mi aspettavo). Il che solleva tre domande: perché c'è una differenza? Si tratta di un bug nell'implementazione ternaria o c'è una ragione per il tipo cast? Dato che c'è una differenza, l'operazione ternaria è più o meno performante di un equivalente if -statement (lo so, la differenza non può essere enorme, ma comunque)?

+6

Credi seriamente che c'è un errore nell'operatore ternario piuttosto che nella * comprensione * dell'uso documentato e delle restrizioni dell'operatore? Quali pensi che siano le probabilità di accadere realisticamente? Considera di cambiare il titolo della tua domanda in "incomprensione su come funziona l'operatore ternario". –

+1

Ecco perché sto chiedendo alla domanda perché il compilatore decide che getNumber() e 0 devono entrambi valutare un int se assegno il risultato a un intero. Per me, non ha assolutamente senso gettare i due argomenti sul più restrittivo dei due tipi PRIMA del confronto piuttosto che sul tipo effettivamente richiesto DOPO il confronto. Perché passare attraverso unboxing e poi reboxing getNumber()? –

+0

Non fa differenza ciò che tu o io riteniamo * debba * accadere. Piuttosto tutto ciò che conta è ciò che è chiaramente documentato nel JLS. –

risposta

13

Secondo JLS: -

Il tipo di un'espressione condizionale è determinato come segue:

  • Se il secondo e terzo operandi hanno lo stesso tipo (che può essere il tipo nullo) , quindi questo è il tipo di espressione condizionale .
  • Se uno dei secondi e terzi operandi è di tipo primitivo T, e il tipo dell'altro è il risultato dell'applicazione della conversione di boxe
    (§5.1.7) a T, il tipo di espressione condizionale è T
+1

Quindi, dovrebbe essere in questo modo. Lascia la domanda se ci sia una differenza di prestazioni tra i due, soprattutto in considerazione di tutti gli eventi di auto- (un) -boxing. –

+0

@Markus .. Beh, non si può dire se ci sia una differenza di prestazioni o meno .. Questo certamente dà al programmatore la facilità di codificare .. Ma sì, poiché questo include una piccola parte di "Unboxing" .. che non è nel caso di if-else .. Quindi la performance potrebbe essere bassa .. –

+0

@Markus .. Ma dato che l'operatore ternario è usato principalmente solo in caso di condizione di istruzione 'single'.Quindi, non è molto preoccupante .. Ovviamente non sarai in grado di spostare l'intera espressione da un blocco if-else a un operatore ternario .. Quindi, entrambi hanno vantaggi e svantaggi. –

11

Il problema è che:

Integer number = (condition) ? getNumber() : 0; 

Forza un unboxing e reinscatolamento del risultato di ControllaNumero(). Questo perché la parte falsa del ternario (0) è un numero intero, quindi tenta di convertire il risultato di getNumber() in un int. Considerando che ciò non è il seguente:

Integer number = (condition) ? getNumber() : (Integer) 0; 

Questo non è un bug, solo il modo in cui Java ha scelto di fare le cose.

+0

Esattamente, ma perché? –

+2

@Markus Vedere [Specifica del linguaggio Java 15.25] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.25) 'Se uno dei secondi e terzi gli operandi sono di tipo primitivo T, e il tipo dell'altro è il risultato dell'applicazione della conversione di boxing (§5.1.7) a T, quindi il tipo di espressione condizionale è T " – halex

2

Questo è come dovrebbe funzionare. L'operatore ternario è non che equivale a una normale dichiarazione if. I corpi di if e else sono dichiarazioni, mentre le parti seguenti ? e : sono espressioni, che sono necessari per valutare allo stesso tipo.

In altre parole: a = b ? c : d non è equivalente a if (b) a = c; else a = d;. Invece, b ? c : d è un'espressione a sé stante e l'assegnazione del suo risultato a a non influirà sul risultato.

Problemi correlati