2014-09-19 5 views
15

Ricevo NullPointerException in un'istanza di seguito mentre la sua controparte è scorrevole.Perché l'operazione ternaria fornisce un punto nullo mentre la sua controparte ifelse no?

public static void main(String[] args){ 
    System.out.println(withTernary(null, null)); //Null Pointer 
    System.out.println(withIfElse(null, null)); //No Exception 
} 

private static Boolean withTernary(String val, Boolean defVal){ 
    return val == null ? defVal : "true".equalsIgnoreCase(val); 
} 

private static Boolean withIfElse(String val, Boolean defVal){ 
    if (val == null) return defVal; 
    else return "true".equalsIgnoreCase(val); 
} 

Online version

Online version with the lines in main reversed, che emette null da withIfElse e poi fallisce in withTernary.

Sto usando seguente versione java

java version "1.6.0_65" 
Java(TM) SE Runtime Environment (build 1.6.0_65-b14-462-11M4609) 
Java HotSpot(TM) 64-Bit Server VM (build 20.65-b04-462, mixed mode) 
+1

Questo è un duplicato. – Raedwald

+2

@ Raedwald: di cosa? Perché non contrassegnarlo come tale? –

+2

Questo è veramente ... dannatamente confuso. Questo è un caso molto particolare nel JLS ... – nneonneo

risposta

9

Ecco la citazione pertinente the spec (§15.25.2):

booleane espressioni condizionali sono espressioni standalone (§15.2).

Il tipo di un'espressione booleana è determinata come segue:

  • Se il secondo e terzo operandi sono entrambi di tipo Boolean, l'espressione condizionale è di tipo Boolean.

  • In caso contrario, l'espressione condizionale ha tipo boolean.

Pertanto, il tipo di espressione complessiva è considerato boolean, e il valore Boolean viene autounboxed, causando un NullPointerException.


Come menzionato nei commenti, perché i seguenti non sollevano un'eccezione?

return val == null ? null : "true".equalsIgnoreCase(val); 

Bene, brano sopra riportato dal spec specificamente si applica solo alle espressioni booleane condizionali, che sono specificati here (§15.25):

Se sia il secondo e il terzo espressioni operandi sono espressioni booleane , l'espressione condizionale è un'espressione condizionale booleana.

Ai fini della classificazione di un condizionale, le seguenti espressioni sono espressioni booleane:

  • Un'espressione di una forma autonoma (§15.2) che è di tipo boolean o Boolean.

  • Un'espressione parentesi boolean (§15.8.5).

  • Un'espressione di creazione istanza di classe (§15.9) per la classe Boolean.

  • Un'espressione invocazione di metodi (§15.12) per il quale il metodo più specifico scelto (§15.12.2.5) ha tipo di ritorno boolean o Boolean.
    (Si noti che per un metodo generico, questo è il tipo prima istanziare argomenti di tipo del metodo.)

  • Un boolean espressione condizionale.

Dal null non è un'espressione booleana , l'espressione condizionale nel complesso non è un booleana espressione condizionale. Facendo riferimento alla Tabella 15.2 (più avanti nella stessa sezione), possiamo vedere che una tale espressione è considerata come un tipo Boolean, quindi non si verifica alcuna cancellazione e nessuna eccezione viene sollevata.

+0

Quindi questo "restituisce val == null? Null:" true ".equalsIgnoreCase (val);" dovrebbe anche fallire ma non è così. Destra? – DKSRathore

+0

@DKSRathore Ho aggiornato la mia risposta per spiegare questo caso. –

4

Perché autounboxing. Java deriva il tipo comune per defVal e "true".equalsIgnoreCase(val). Il primo tipo è Booleano, ma il secondo è booleano. Per ragioni sconosciute, il tipo comune sarà booleano (puoi trovare la regola nelle specifiche).

7

val == null ? defVal : "true".equalsIgnoreCase(val) - in questa espressione, il terzo argomento è boolean, e poiché l'operatore ternario deve avere un tipo statico, si cercherà di unboxing il null, quindi la NPE. Solo l'assegnazione a Boolean causerà di nuovo il pugilato. Verifica this.

+0

Non trovo NullPointer in return val == null? null: "true" .equalsIgnoreCase (val) ;. Perché? – DKSRathore

+0

L'NPE si verifica quando si è provato con Boolean. Il compilatore ha provato a rimuovere un box 'Boolean' con valore null su 'boolean'. Da qui l'NPE. Qui il terzo operando ha valore corretto, semplice – RahulArackal

3

Questo potrebbe essere spiegato se defVal è null.

Con espressioni ternari, entrambe le opzioni devono essere esattamente dello stesso tipo e, se non viene applicata una certa coercizione:

Il JLS 15.25 "Conditional operator ? :" dice:

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

Nel caso di questa espressione:

val == null ? defVal : "true".equalsIgnoreCase(val) 

Il valore Boolean per defVal è auto-unboxing per abbinare il boolean risultato del confronto di stringhe. Questo nonostante il fatto che il risultato del ternario venga automaticamente ricondotto a Boolean - quando decide come eseguire il cast, un ternario non considera nulla al di fuori di se stesso.

2

Ok. Proviamo in questo modo

public static void main(String[] args){ 
    System.out.println(withTernary(null, null)); 
} 

private static Boolean withTernary(String val, Boolean defVal){ 
    return (val == null ? defVal : Boolean.valueOf("true".equalsIgnoreCase(val))); 
} 

Ora funzionerà correttamente. Ora non c'è lo unboxing qui e non ti daremo un'eccezione. Ma in altro modo a causa di nullunbox otterrete NPE

public static void main(String[] args){ 
    System.out.println(withTernary(null, null)); //Null Pointer 
} 

private static Boolean withTernary(String val, Boolean defVal){ 
    return (val == null ? defVal : "true".equalsIgnoreCase(val)); 
} 
Problemi correlati