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?

È stato utile?

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 e boolean, 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 a boolean. Il compilatore inserisce codice di auto-unboxing al 2 ° operando (valore di ritorno di returnsNull()) per renderlo tipo boolean. Questo, naturalmente, fa sì che il NPE dal null restituita in fase di esecuzione.

  • Per E2, tipi di 2 ° e 3 ° operandi sono <special null type> (non Boolean come in E1!) E boolean 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 a Boolean. Il codice di auto-boxing compilatore inserisce per il 3 ° operando (false). Il 2 ° operando non ha bisogno di auto-unboxing come in E1, in modo che nessun NPE auto-unboxing quando viene restituito null.


Questa domanda ha bisogno di una simile analisi di tipo:

Java operatore condizionale:? Tipo di risultato

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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top