Pourquoi ce jette 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;
}
alors que ce n'est pas
public static void main(String[] args) throws Exception {
Boolean b = true ? null : false;
System.out.println(b); // null
}
?
La solution est d'ailleurs de remplacer false
par Boolean.FALSE
pour éviter d' null
être déballé boolean
- ce qui n'est pas possible. Mais ce n'est pas la question. La question est pourquoi ? Existe-t-il des références dans JLS qui confirment ce comportement, en particulier du 2ème cas?
Réponses:
La différence est que le type explicite de la
returnsNull()
méthode affecte le typage statique des expressions au moment de la compilation:Voir Spécification du langage Java, section 15.25 Opérateur conditionnel? :
Pour E1, les types des 2ème et 3ème opérandes sont
Boolean
etboolean
respectivement, donc cette clause s'applique:Puisque le type de l'expression est
boolean
, le 2ème opérande doit être forcéboolean
. Le compilateur insère du code de déballage automatique dans le 2ème opérande (valeur de retour dereturnsNull()
) pour le faire taperboolean
. Cela provoque bien sûr le NPE dunull
retourné au moment de l'exécution.Pour E2, les types des 2ème et 3ème opérandes sont
<special null type>
(pasBoolean
comme dans E1!) Etboolean
respectivement, donc aucune clause de typage spécifique ne s'applique ( allez les lire! ), Donc la clause finale "sinon" s'applique:<special null type>
(voir §4.1 )boolean
<special null type>
(voir le dernier élément de la liste des conversions de boxe au §5.1.7 )Boolean
Ainsi, le type de l'expression conditionnelle est
Boolean
et le 3ème opérande doit être contraintBoolean
. Le compilateur insère du code de boxing automatique pour le 3ème opérande (false
). Le 2ème opérande n'a pas besoin du déballage automatique comme dansE1
, donc pas de NPE de déballage automatique lorsqu'ilnull
est renvoyé.Cette question nécessite une analyse de type similaire:
Opérateur conditionnel Java?: Type de résultat
la source
lub
en questionlub(T1,T2)
?La ligne:
est transformé en interne en:
pour effectuer le déballage; donc:
null.booleanValue()
donnera un NPEC'est l'un des principaux écueils lors de l'utilisation de l'auto-box. Ce comportement est en effet documenté dans 5.1.8 JLS
Edit: Je pense que le déballage est dû au fait que le troisième opérateur est de type booléen, comme (cast implicite ajouté):
la source
À partir de la spécification du langage Java, section 15.25 :
Ainsi, le premier exemple essaie d'appeler
Boolean.booleanValue()
afin de convertirBoolean
àboolean
selon la première règle.Dans le second cas, le premier opérande est de type nul, lorsque le second n'est pas de type référence, la conversion automatique est donc appliquée:
la source
null
.boolean
n'est pas un type de référence.Nous pouvons voir ce problème à partir du code d'octet. À la ligne 3 du code d'octet principal,,
3: invokevirtual #3 // Method java/lang/Boolean.booleanValue:()Z
le booléen de boxe de valeur null,invokevirtual
la méthodejava.lang.Boolean.booleanValue
, il lancera NPE bien sûr.la source