Examinons le code Java simple dans l'extrait de code suivant:
public class Main {
private int temp() {
return true ? null : 0;
// No compiler error - the compiler allows a return value of null
// in a method signature that returns an int.
}
private int same() {
if (true) {
return null;
// The same is not possible with if,
// and causes a compile-time error - incompatible types.
} else {
return 0;
}
}
public static void main(String[] args) {
Main m = new Main();
System.out.println(m.temp());
System.out.println(m.same());
}
}
Dans ce code Java le plus simple, la temp()
méthode n'émet aucune erreur du compilateur même si le type de retour de la fonction est int
, et nous essayons de renvoyer la valeur null
(via l'instruction return true ? null : 0;
). Une fois compilé, cela provoque évidemment l'exception d'exécution NullPointerException
.
Cependant, il semble que la même chose ne va pas si nous représentons l'opérateur ternaire avec une if
instruction (comme dans la same()
méthode), ce qui génère une erreur de compilation! Pourquoi?
int foo = (true ? null : 0)
et lesnew Integer(null)
deux se compilent bien, le second étant la forme explicite de la boîte automatique.null
àInteger
... Ce serait regarder comme « deviner » pour moi ou « faire des choses travail » ...Integer foo() { return "1"; }
ne compile pas.)Réponses:
Le compilateur interprète
null
comme une référence nulle à anInteger
, applique les règles de mise en boîte automatique / unboxing pour l'opérateur conditionnel (comme décrit dans la spécification du langage Java, 15.25 ), et avance avec bonheur. Cela générera unNullPointerException
au moment de l'exécution, que vous pouvez confirmer en l'essayant.la source
capture conversion
etlub(T1,T2)
) ?? Aussi, est-il vraiment possible d'appliquer la boxe à une valeur nulle? Ne serait-ce pas comme "deviner" ??lub(T1,T2)
est le type de référence le plus spécifique commun dans la hiérarchie des types de T1 et T2. (Ils partagent tous les deux au moins Object, il y a donc toujours un type de référence plus spécifique.)null
n'est pas encadré dans un Integer, il est interprété comme une référence à un Integer (une référence nulle, mais ce n'est pas un problème). Aucun objet Integer n'est construit à partir de la valeur null, il n'y a donc aucune raison pour une NumberFormatException.null
(qui n'est pas un type numérique primitif), la clause applicable est "Si p est une valeur d'un autre type, la conversion de boxe équivaut à une conversion d'identité ". Donc, la conversion boxing denull
enInteger
rendementsnull
, sans invoquer aucunInteger
constructeur.Je pense que le compilateur Java interprète
true ? null : 0
comme uneInteger
expression, qui peut être implicitement convertie enint
, éventuellement en donnantNullPointerException
.Pour le second cas, l'expression
null
est du type spécial nul , voir , donc le codereturn null
crée une incompatibilité de type.la source
true ? null : 0
commeInteger
? Par autoboxing d'0
abord ??En fait, tout est expliqué dans la spécification du langage Java .
Par conséquent, le "null" dans votre
(true ? null : 0)
obtient un type int, puis est automatiquement converti en Integer.Essayez quelque chose comme ceci pour vérifier cela
(true ? null : null)
et vous obtiendrez l'erreur du compilateur.la source
int
valeur de la fonction, ce qui provoque un NPE.null
àInteger
avecnew Integer(null);
« Soit T1 du type résultant de l' application de conversion de boxe à S1 ... » vous obtiendrez unNumberFormatException
et ce n'est pas le cas ...Dans le cas de l'
if
instruction, lanull
référence n'est pas traitée comme uneInteger
référence car elle ne participe pas à une expression qui la force à être interprétée comme telle. Par conséquent, l'erreur peut être facilement détectée au moment de la compilation car il s'agit plus clairement d'une erreur de type .En ce qui concerne l'opérateur conditionnel, la spécification du langage Java §15.25 «Conditional Operator
? :
» répond bien à cela dans les règles d'application de la conversion de type:la source
0
est automatiquement renvoyé,Integer
le compilateur exécute le dernier cas des "règles d'opérateur ternaire" comme décrit dans la spécification du langage Java. Si c'est vrai, alors il m'est difficile de croire que cela passerait alors au cas 3 des mêmes règles qui ont un type null et un type de référence qui font que la valeur de retour de l'opérateur ternaire est le type de référence (Integer). .Integer
? C'est exactement ce qui se passe; le NPE est généré en essayant de déballer la valeur de l'expression afin de renvoyer un àint
partir de la fonction. Changez la fonction pour renvoyer unInteger
et il reviendranull
sans problème.null
tombe dans cette catégorie . De plus, nous passerions ensuite à l'étape "Sinon, la promotion numérique binaire (§5.6.2) est appliquée ... Notez que la promotion numérique binaire effectue la conversion de déballage (§5.1.8) ..." pour déterminer le type de retour. Mais la conversion unboxing générerait un NPE et cela ne se produit qu'au moment de l'exécution et non en essayant de déterminer le type d'opérateur ternaire. Je suis toujours confus ..null
est traité comme s'il avait du typeint
, mais est en fait équivalent àthrow new NullPointerException()
, c'est tout.La première chose à garder à l'esprit est que les opérateurs ternaires Java ont un "type", et que c'est ce que le compilateur déterminera et considérera quels que soient les types réels / réels du deuxième ou du troisième paramètre. En fonction de plusieurs facteurs, le type d'opérateur ternaire est déterminé de différentes manières, comme illustré dans la spécification du langage Java 15.26
Dans la question ci-dessus, nous devrions considérer le dernier cas:
C'est de loin le cas le plus complexe une fois que vous regardez l' application de la conversion de capture (§5.1.10) et surtout à lub (T1, T2) .
En anglais simple et après une simplification extrême, nous pouvons décrire le processus comme le calcul de la "Superclasse la moins commune" (oui, pensez au LCM) des deuxième et troisième paramètres. Cela nous donnera l'opérateur ternaire "type". Encore une fois, ce que je viens de dire est une simplification extrême (considérez les classes qui implémentent plusieurs interfaces communes).
Par exemple, si vous essayez ce qui suit:
Vous remarquerez que le type résultant de l'expression conditionnelle est
java.util.Date
puisqu'il s'agit de la "Superclasse la moins commune" pour la paireTimestamp
/Time
.Puisqu'il
null
peut être autoboxé à n'importe quoi, la "Superclasse la moins commune" est laInteger
classe et ce sera le type de retour de l'expression conditionnelle (opérateur ternaire) ci-dessus. La valeur de retour sera alors un pointeur nul de typeInteger
et c'est ce qui sera retourné par l'opérateur ternaire.Au moment de l'exécution, lorsque la machine virtuelle Java déballe, le
Integer
aNullPointerException
est lancé. Cela se produit car la machine virtuelle Java tente d'appeler la fonctionnull.intValue()
, oùnull
est le résultat de la détection automatique.À mon avis (et comme mon avis n'est pas dans la spécification du langage Java, beaucoup de gens trouveront cela faux de toute façon), le compilateur fait un mauvais travail en évaluant l'expression dans votre question. Étant donné que vous avez écrit,
true ? param1 : param2
le compilateur doit déterminer tout de suite que le premier paramètre -null
- sera renvoyé et il doit générer une erreur de compilation. C'est un peu similaire à lorsque vous écrivezwhile(true){} etc...
et que le compilateur se plaint du code sous la boucle et le marque avecUnreachable Statements
.Votre deuxième cas est assez simple et cette réponse est déjà trop longue ...;)
CORRECTION:
Après une autre analyse, je crois que j'avais tort de dire qu'une
null
valeur peut être encadrée / auto-indexée sur n'importe quoi. En parlant de la classe Integer, la boxe explicite consiste à invoquer lenew Integer(...)
constructeur ou peut-être leInteger.valueOf(int i);
(j'ai trouvé cette version quelque part). Le premier lancerait unNumberFormatException
(et cela n'arrive pas) tandis que le second n'aurait tout simplement pas de sens car unint
ne peut pas êtrenull
...la source
null
code original de l'OP n'est pas encadré. La façon dont cela fonctionne est: le compilateur suppose que lenull
est une référence à un entier. En utilisant les règles des types d'expressions ternaires, il décide que l'expression entière est une expression Integer. Il génère ensuite du code pour placer automatiquement le1
(au cas où la condition serait évaluée àfalse
). Au cours de l'exécution, la condition est évaluée commetrue
telle pour l'expressionnull
. Lorsque vous essayez de renvoyer unint
depuis la fonction, lenull
est déballé. Cela jette alors un NPE. (Le compilateur pourrait optimiser la plupart de cela.)En fait, dans le premier cas, l'expression peut être évaluée, puisque le compilateur sait, qu'elle doit être évaluée comme un
Integer
, mais dans le second cas, le type de la valeur de retour (null
) ne peut pas être déterminé, donc il ne peut pas être compilé. Si vous le convertissez enInteger
, le code sera compilé.la source
la source
Que dis-tu de ça:
La sortie est vraie, vraie.
La couleur Eclipse code le 1 dans l'expression conditionnelle comme une boîte automatique.
Je suppose que le compilateur voit le type de retour de l'expression comme Object.
la source