Frage

Warum diese throw 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;
}

, während dies nicht

public static void main(String[] args) throws Exception {
    Boolean b = true ? null : false;
    System.out.println(b); // null
}

Die Lösung ist übrigens false von Boolean.FALSE zu vermeiden null seinem unboxed zu boolean ersetzen --which nicht möglich ist. Aber das ist nicht die Frage. Die Frage ist Warum ? Gibt es Referenzen in JLS, die dieses Verhalten, vor allem des zweiten Fall bestätigt?

War es hilfreich?

Lösung

Der Unterschied besteht darin, dass der explizite Typ der returnsNull() Methode der statische Typisierung der Ausdrücke bei der Kompilierung beeinflusst:

E1: `true ? returnsNull() : false` - boolean (auto-unboxing 2nd operand to boolean)

E2: `true ? null : false` - Boolean (autoboxing of 3rd operand to Boolean)

Siehe Java Language Specification, Abschnitt 15.25 Conditional Operator? :

  • Für E1, die Typen der 2. und 3. Operanden sind Boolean und boolean jeweils, so dass diese Klausel gilt:

      

    Wenn einer der zweiten und dritten Operanden vom Typ boolean ist und die Art des anderen ist vom Typ Boolean, dann der Typ des bedingten Ausdrucks ist boolean.

    Da der Typ des Ausdrucks boolean ist, muss der zweite Operand boolean dazu gezwungen werden. Die Compiler fügt Auto-Unboxing Code zum zweiten Operanden (Rückgabewert von returnsNull()), um es boolean zu machen eingeben. Dies ist natürlich bewirkt, dass die NPE vom null zur Laufzeit zurückgegeben.

  • Für E2, Typen des 2. und 3. Operanden sind <special null type> (nicht Boolean wie in E1!) Und boolean jeweils, so dass keine spezifische Typisierung Klausel gilt ( go lesen ‚em ), die letzte so! "gilt sonst" Klausel:

      

    Ansonsten sind die zweiten und dritten Operanden sind von Typen S1 und S2 jeweils. Lassen T1 die Art sein, dass die Ergebnisse von der Anwendung zu S1 boxing Konvertierung und lass T2 die Art sein, dass die Ergebnisse aus Boxen Umwandlung zu S2 angelegt wird. Die Art des konditionalen Ausdrucks ist das Ergebnis des Anwendens capture Umwandlung (§5.1.10) zu lub (T1, T2) (§15.12.2.7).

    • S1 == <special null type> (siehe §4.1 )
    • S2 == boolean
    • T1 == Box (S1) == <special null type> (siehe letzte Element in der Liste der Boxen Konvertierungen in §5.1.7 )
    • T2 == Box (S2) == `Boolean
    • lub (T1, T2) == Boolean

    So der Typ des bedingten Ausdrucks Boolean und der dritte Operand muss Boolean dazu gezwungen werden. Der Compiler fügt Auto-Boxen-Code für den dritten Operanden (false). Der zweite Operand muss nicht den Auto-Unboxing wie in E1, so dass kein Auto-Unboxing NPE wenn null zurückgeführt wird.


Diese Frage braucht eine ähnliche Analyse:

Java Konditionaloperator: Ergebnistyp

Andere Tipps

Die Zeile:

    Boolean b = true ? returnsNull() : false;

wird intern umgewandelt:

    Boolean b = true ? returnsNull().booleanValue() : false; 

das Unboxing auszuführen; also: null.booleanValue() eine NPE ergibt

Dies ist eine der wichtigsten Gefahren beim Autoboxing verwenden. Dieses Verhalten ist in der Tat dokumentiert in 5.1.8 JLS

Edit: Ich glaube, das Unboxing mit dem dritten Operator gebührt der boolean-Typ ist, wie (implizite Umwandlung hinzugefügt):

   Boolean b = (Boolean) true ? true : false; 

Java Language Specification, Abschnitt 15.25 :

  
      
  • Wenn eine der zweiten und dritten   Operanden sind vom Typ boolean und   Typ des anderen ist vom Typ Boolean,   dann wird der Typ der bedingten   Ausdruck ist boolean.
  •   

So, das erste Beispiel versucht Boolean.booleanValue() zu konvertieren, um Boolean zu boolean gemäß der ersten Regel zu nennen.

Im zweiten Fall ist der erste Operand der Null-Typ, wenn der zweite nicht der Referenztyp ist, so wird Autoboxing Umwandlung angewendet:

  
      
  • Ansonsten ist die zweite und dritte   Operanden sind von Typen S1 und S2   beziehungsweise. Lassen T1 der Typ sein,   Ergebnisse aus der Anwendung Boxen   Umwandlung zu S1, und lassen T2 werden die   geben, dass die Ergebnisse aus der Anwendung Boxen   Umwandlung in S2. Die Art der   Bedingungsausdruck ist das Ergebnis   die Anwendung Capture-Umwandlung   (§5.1.10) zu lub (T1, T2) (§15.12.2.7).
  •   

Wir können dieses Problem von Byte-Code sehen. In Zeile 3 von Bytecode Haupt, 3: invokevirtual #3 // Method java/lang/Boolean.booleanValue:()Z, der Box-Boolean von Wert null, invokevirtual die Methode java.lang.Boolean.booleanValue, wird es NPE natürlich werfen.

    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
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top