Je crois que c'était un bug qui semble avoir été corrigé. Jetant un NullPointerException
semble être le comportement, en fonction de la JLS.
Je pense que ce qui se passe ici est que, pour une raison quelconque dans la version 8, le compilateur considère les limites de la variable type mentionné par le retour de la méthode de type plutôt que sur le type d'arguments. En d'autres termes, il pense qu' ...get("1")
retours Object
. Ce pourrait être parce que c'est compte tenu de la méthode d'effacement, ou quelque autre raison.
Le comportement devrait s'appuyer sur le type de retour de la get
méthode, comme spécifié ci-dessous des extraits d' §15.26:
-
Si le deuxième et le troisième opérande expressions numériques expressions, l'expression conditionnelle est un numérique, expression conditionnelle.
Pour le classement sous condition, les expressions suivantes sont des expressions numériques:
[...]
-
Une invocation de méthode d'expression (§15.12) pour qui la choisi le plus spécifique de la méthode (§15.12.2.5) a un type de retour qui est convertible en un type numérique.
Notez que, pour une méthode générique, c'est le type avant de l'instanciation de la méthode du type des arguments.
[...]
Sinon, l'expression conditionnelle est une référence de l'expression conditionnelle.
[...]
Le type de numérique à l'expression conditionnelle est déterminé comme suit:
[...]
Si l'un des deuxième et troisième opérandes est de type primitif T
, et le type de l'autre est le résultat de l'application de la boxe de conversion (§5.1.7) à l' T
, alors le type de l'expression conditionnelle est - T
.
En d'autres termes, si les deux expressions sont convertibles à un type numérique, et l'un est primitif et l'autre est enfermé dans une boîte, puis le type de résultat de l'expression conditionnelle est le type de primitive.
(Tableau de 15,25 C aussi idéalement nous montre que le type d'une expression ternaire boolean ? double : Double
serait en effet double
, nouveau sens unboxing et les vomissements est correct).
Si le type de retour de la get
méthode n'était pas convertible à un type numérique, puis le ternaire conditionnelle serait considéré comme une "référence de l'expression conditionnelle" et unboxing ne pas se produire.
Aussi, je pense que la note "pour une méthode générique, c'est le type avant l'instanciation de la méthode des arguments de type" ne devrait pas s'appliquer à notre cas. Map.get
ne pas déclarer des variables de type, il n'est donc pas une méthode générique par JL' définition. Toutefois, la présente note a été ajoutée dans Java 9 (étant le seul changement, voir JLS8), il est donc possible qu'il ait quelque chose à voir avec le comportement que nous observons aujourd'hui.
Pour un HashMap<String, Double>
, le type de retour d' get
devrait être Double
.
Voici un MCVE l'appui de ma théorie que le compilateur est en considérant le type de la variable limites plutôt que sur le type d'arguments:
class Example<N extends Number, D extends Double> {
N nullAsNumber() { return null; }
D nullAsDouble() { return null; }
public static void main(String[] args) {
Example<Double, Double> e = new Example<>();
try {
Double a = false ? 0.0 : e.nullAsNumber();
System.out.printf("a == %f%n", a);
Double b = false ? 0.0 : e.nullAsDouble();
System.out.printf("b == %f%n", b);
} catch (NullPointerException x) {
System.out.println(x);
}
}
}
La sortie de ce programme sur Java 8 est:
a == null
java.lang.NullPointerException
En d'autres termes, malgré e.nullAsNumber()
et e.nullAsDouble()
ayant le même type de retour, seulement e.nullAsDouble()
est considéré comme une "expression numérique". La seule différence entre les méthodes est le type de variable liée.
Il y a probablement plus d'enquête qui pourrait être fait, mais je voulais poster mes résultats. J'ai essayé pas mal de choses et a constaté que le bug (c'est à dire pas unboxing/NPE) semble se produire uniquement lorsque l'expression est une méthode avec un type de variable dans le type de retour.
Fait intéressant, j'ai trouvé que le programme suivant met aussi en Java 8:
import java.util.*;
class Example {
static void accept(Double d) {}
public static void main(String[] args) {
accept(false ? 1.0 : new HashMap<String, Double>().get("1"));
}
}
Qui montre que le compilateur du comportement est en fait différent, selon que le ternaire expression est affectée à une variable locale ou un paramètre d'une méthode.
(À l'origine je voulais utiliser les surcharges de prouver le type réel que le compilateur est de donner à l'expression de l'expression, mais il ne ressemble pas à ce qui est possible compte tenu de la différence susmentionnée. Il est possible, il ya encore une autre façon que je n'ai pas pensé, cependant).