Booleani, operatori condizionali e autoboxing
-
28-09-2019 - |
Domanda
Perché questo lancia NullPointerException
public static void main(String[] args) throws Exception {
Boolean b = true ? returnsNull() : false; // NPE on this line.
System.out.println(b);
}
public static Boolean returnsNull() {
return null;
}
mentre questo no
public static void main(String[] args) throws Exception {
Boolean b = true ? null : false;
System.out.println(b); // null
}
?
La soluzione è comunque sostituire false
di Boolean.FALSE
evitare null
essere unboxed a boolean
– il che non è possibile.Ma non è questa la domanda.La domanda è Perché?Ci sono riferimenti in JLS che confermano questo comportamento, soprattutto nel 2° caso?
Soluzione
La differenza è che il tipo esplicito del metodo returnsNull()
influenza la tipizzazione statica delle espressioni in fase di compilazione:
E1: `true ? returnsNull() : false` - boolean (auto-unboxing 2nd operand to boolean)
E2: `true ? null : false` - Boolean (autoboxing of 3rd operand to Boolean)
Vedere Java Language Specification, sezione 15.25 condizionale operatore? :
-
Per E1, i tipi di 2 ° e 3 ° operandi sono rispettivamente
Boolean
eboolean
, quindi si applica questa clausola:Se uno dei secondi e terzi operandi è di tipo boolean e il tipo di l'altro è di tipo booleano, allora il tipo dell'espressione condizionale è booleana.
Dal momento che il tipo di espressione è
boolean
, il 2 ° operando deve essere costretto aboolean
. Il compilatore inserisce codice di auto-unboxing al 2 ° operando (valore di ritorno direturnsNull()
) per renderlo tipoboolean
. Questo, naturalmente, fa sì che il NPE dalnull
restituita in fase di esecuzione. -
Per E2, tipi di 2 ° e 3 ° operandi sono
<special null type>
(nonBoolean
come in E1!) Eboolean
rispettivamente, in modo alcuna clausola specifica tipizzazione applica ( andare a leggere 'em ), in modo che il finale! "altrimenti" si applica la clausola:In caso contrario, il secondo e terzo operandi sono di tipo S1 e S2, rispettivamente. Lasciare T1 sia del tipo che risulta dalla applicazione di conversione boxe per S1, e lasciare T2 essere del tipo che risulta dalla applicazione di conversione boxe a S2. Il tipo dell'espressione condizionale è il risultato dell'applicazione di conversione cattura (§5.1.10) a lub (T1, T2) (§15.12.2.7).
- S1 ==
<special null type>
(vedi §4.1 ) - S2 ==
boolean
- T1 == scatola (S1) ==
<special null type>
(vedi ultima voce nella lista delle conversioni boxe in §5.1.7 ) - T2 == scatola (S2) == `booleana
- lub (T1, T2) ==
Boolean
Così il tipo dell'espressione condizionale è
Boolean
e il 3 ° operando deve essere costretto aBoolean
. Il codice di auto-boxing compilatore inserisce per il 3 ° operando (false
). Il 2 ° operando non ha bisogno di auto-unboxing come inE1
, in modo che nessun NPE auto-unboxing quando viene restituitonull
. - S1 ==
Questa domanda ha bisogno di una simile analisi di tipo:
Altri suggerimenti
La linea:
Boolean b = true ? returnsNull() : false;
viene trasformato internamente in:
Boolean b = true ? returnsNull().booleanValue() : false;
eseguire l'unboxing;così: null.booleanValue()
produrrà una NPE
Questa è una delle principali insidie quando si utilizza l'autoboxing.Questo comportamento è infatti documentato in 5.1.8JLS
Modificare:Credo che l'unboxing sia dovuto al fatto che il terzo operatore è di tipo booleano, come (cast implicito aggiunto):
Boolean b = (Boolean) true ? true : false;
Java Language Specification, sezione 15.25 :
- Se uno di secondo e terzo operandi è di tipo boolean e digitare degli altri è di tipo booleano, quindi il tipo di condizionale espressione è booleana.
Quindi, il primo esempio tenta di chiamare Boolean.booleanValue()
al fine di convertire Boolean
a boolean
come per la prima regola.
Nel secondo caso il primo operando è di tipo nullo, quando il secondo non è del tipo di riferimento, quindi conversione autoboxing viene applicato:
- In caso contrario, il secondo e terzo operandi sono di tipo S1 e S2 rispettivamente. Lasciare T1 sia il tipo che i risultati di applicazione di boxe conversione S1, e lasciare che sia il T2 tipo che i risultati di applicazione di boxe conversione in S2. Il tipo di espressione condizionale è il risultato della conversione cattura applicare (§5.1.10) per LUB (T1, T2) (§15.12.2.7).
Si può vedere questo problema dal bytecode. Alla linea 3 del codice di byte di principale, 3: invokevirtual #3 // Method java/lang/Boolean.booleanValue:()Z
, la boxe booleano di valore nullo, il metodo invokevirtual
java.lang.Boolean.booleanValue
, si getterà NPE naturalmente.
public static void main(java.lang.String[]) throws java.lang.Exception;
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: invokestatic #2 // Method returnsNull:()Ljava/lang/Boolean;
3: invokevirtual #3 // Method java/lang/Boolean.booleanValue:()Z
6: invokestatic #4 // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
9: astore_1
10: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
13: aload_1
14: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
17: return
LineNumberTable:
line 3: 0
line 4: 10
line 5: 17
Exceptions:
throws java.lang.Exception
public static java.lang.Boolean returnsNull();
descriptor: ()Ljava/lang/Boolean;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: aconst_null
1: areturn
LineNumberTable:
line 8: 0