Я считаю, что это была ошибка, которая, кажется, была исправлена.Бросок NullPointerException
кажется правильным поведением, согласно JLS.
Я думаю, что здесь происходит то, что по какой-то причине в версии 8 компилятор рассмотрел границы упомянутой переменной типапо типу возврата метода, а не фактические аргументы типа.Другими словами, он думает, что ...get("1")
возвращает Object
.Это может быть связано с удалением метода или по какой-либо другой причине.
Поведение должно зависеть от типа возврата метода get
, как указано в приведенных ниже выдержках из §15.26 :
Если и второе, и третье выражения операнда являются числовыми выражениями, условное выражение является числовым условным выражением.
Для классификации условия следующие выражения являются числовыми выражениями:
[…]
Выражение вызова метода(§15.12), для которого выбранный наиболее конкретный метод (§15.12.2.5) имеет тип возвращаемого значения, который можно преобразовать в числовой тип.
Обратите внимание, что для универсального метода этоявляется типом перед созданием аргументов типа метода.
[…]
В противном случае, условныйвыражение является условным условным выражениемression.
[…]
Тип числового условного выражения определяется следующим образом:
Другими словами, если оба выражения могут быть преобразованы в числовой тип, и одно является примитивным, а другоев штучной упаковке, тогда тип результата троичного условия является примитивным типом.
(Таблица 15.25-C также удобно показывает нам, что тип троичного выражения boolean ? double : Double
действительно будет double
, снова означаяраспаковка и бросание - это правильно.)
Если тип возвращаемого значения метода get
не может быть преобразован в числовой тип, то троичное условное выражение будет считаться «условным условным выражением», а распаковка не будетпроисходят.
Кроме того, я думаю, что примечание «для универсального метода, это тип перед созданием аргументов типа метода» не должно применяться к нашему случаю.Map.get
не объявляет переменные типа, , поэтому это не универсальный метод по определению JLS .Однако это примечание было добавлено в Java 9 (единственное изменение, см. JLS8 ), поэтому возможно, что оно имеет какое-то отношение к поведению, которое мы наблюдаем сегодня.
Для HashMap<String, Double>
тип возвращаемого значения get
должен быть Double
.
Вот MCVE, поддерживающий мою теорию, что компилятор рассматривает переменную типаграницы, а не фактические аргументы типа:
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);
}
}
}
Вывод этой программы на Java 8 :
a == null
java.lang.NullPointerException
Другими словами, несмотря на e.nullAsNumber()
и e.nullAsDouble()
, имеющий тот же фактический тип возврата, только e.nullAsDouble()
рассматривается как "числовое выражение".Единственное различие между методами заключается в привязке переменной типа.
Вероятно, можно провести дополнительное расследование, но я хотел опубликовать свои выводы.Я перепробовал несколько вещей и обнаружил, что ошибка (то есть отсутствие распаковки / NPE), по-видимому, происходит, только когда выражение является методом с переменной типа в возвращаемом типе.
Интересно, я 'мы обнаружили, что следующая программа также выдает в 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"));
}
}
Это показывает, что поведение компилятора на самом деле отличается, в зависимости от того, назначено ли троичное выражение локальной переменной или параметру метода.
(Изначально я хотел использовать перегрузки, чтобы доказать фактический тип, который использует компилятордает троичное выражение, но не похоже, что это возможно, учитывая вышеуказанное различие. Возможно, есть еще один способ, о котором я даже не думал.)