Warum wirft das 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 tut
public static void main(String[] args) throws Exception {
Boolean b = true ? null : false;
System.out.println(b); // null
}
?
Die Lösung besteht im Übrigen darin , sie zu ersetzen, false
um Boolean.FALSE
zu vermeiden null
, dass sie boolean
entpackt wird - was nicht möglich ist. Das ist aber nicht die Frage. Die Frage ist warum ? Gibt es in JLS Referenzen, die dieses Verhalten bestätigen, insbesondere im zweiten Fall?
Antworten:
Der Unterschied besteht darin, dass der explizite Typ der
returnsNull()
Methode die statische Typisierung der Ausdrücke zur Kompilierungszeit beeinflusst:Siehe Java-Sprachspezifikation, Abschnitt 15.25 Bedingter Operator? ::
Für E1, die Typen der 2. und 3. Operanden sind
Boolean
undboolean
jeweils, so dass diese Klausel gilt:Da der Typ des Ausdrucks ist
boolean
, muss der 2. Operand dazu gezwungen werdenboolean
. Der Compiler fügt Auto-Unboxing-Code in den 2. Operanden (Rückgabewert vonreturnsNull()
) ein, um ihn zum Typ zu machenboolean
. Dies führt natürlich dazu, dass die NPEnull
zur Laufzeit zurückgegeben wird.Für E2 sind die Typen des 2. und 3. Operanden
<special null type>
(nichtBoolean
wie in E1!) Undboolean
daher gilt keine spezifische Typisierungsklausel ( lesen Sie sie! ), Daher gilt die letzte "ansonsten" -Klausel:<special null type>
(siehe §4.1 )boolean
<special null type>
(siehe letzten Punkt in der Liste der Boxumwandlungen in §5.1.7 )Boolean
Der Typ des bedingten Ausdrucks ist also
Boolean
und der 3. Operand muss dazu gezwungen werdenBoolean
. Der Compiler fügt Auto-Boxing-Code für den 3. Operanden (false
) ein. Der 2. Operand benötigt nicht das automatische Entpacken wie inE1
, daher wird kein NPE für das automatische Entpackennull
zurückgegeben.Diese Frage erfordert eine ähnliche Typanalyse:
Java-Bedingungsoperator ?: Ergebnistyp
quelle
lub
inlub(T1,T2)
Standplatz?Die Linie:
wird intern umgewandelt in:
das Unboxing durchführen; also:
null.booleanValue()
ergibt eine NPEDies ist eine der größten Gefahren bei der Verwendung von Autoboxing. Dieses Verhalten ist in der Tat in 5.1.8 JLS dokumentiert
Bearbeiten: Ich glaube, das Unboxing ist darauf zurückzuführen, dass der dritte Operator vom booleschen Typ ist, wie (implizite Umwandlung hinzugefügt):
quelle
Aus der Java-Sprachspezifikation, Abschnitt 15.25 :
So ist das erste Beispiel versucht anzurufen ,
Boolean.booleanValue()
um zu konvertieren ,Boolean
umboolean
gemäß der ersten Regel.Im zweiten Fall ist der erste Operand vom Nulltyp, wenn der zweite nicht vom Referenztyp ist, sodass die Autoboxing-Konvertierung angewendet wird:
quelle
null
.boolean
es kein Referenztyp ist.Wir können dieses Problem anhand des Bytecodes erkennen. In Zeile 3 des Hauptbytecodes
3: invokevirtual #3 // Method java/lang/Boolean.booleanValue:()Z
, dem Box-Booleschen Wert Null,invokevirtual
der Methodejava.lang.Boolean.booleanValue
, wird natürlich NPE ausgelöst.quelle