Давайте посмотрим на простой код Java в следующем фрагменте:
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());
}
}
В этом простейшем Java-коде temp()
метод не выдает ошибку компилятора, хотя возвращаемый тип функции - это int
, и мы пытаемся вернуть значение null
(через оператор return true ? null : 0;
). При компиляции это, очевидно, вызывает исключение времени выполнения NullPointerException
.
Тем не менее, оказывается , что то же самое , это не так , если мы представляем трехкомпонентный оператор с if
утверждением (как в same()
методе), который делает выдает ошибку во время компиляции! Зачем?
int foo = (true ? null : 0)
иnew Integer(null)
как скомпилироваться, второе явный вид Autoboxing.null
чтобыInteger
... Это будет выглядеть так же , как «угадывание» мне или «делать вещи работу» ...Integer foo() { return "1"; }
не скомпилируется.)Ответы:
Компилятор интерпретирует
null
как нулевую ссылку на anInteger
, применяет правила автоматической блокировки / распаковки для условного оператора (как описано в спецификации языка Java, 15.25 ) и успешно перемещается дальше. Это создастNullPointerException
во время выполнения, что вы можете подтвердить, попробовав его.источник
capture conversion
иlub(T1,T2)
) ?? Кроме того, действительно ли можно применить бокс к нулевому значению? Разве это не похоже на "угадывание" ??lub(T1,T2)
является наиболее конкретным ссылочным типом, общим в иерархии типов T1 и T2. (У них обоих есть хотя бы один объект, поэтому всегда существует самый специфический тип ссылки.)null
не упакован в целое число, он интерпретируется как ссылка на целое число (пустая ссылка, но это не проблема). Целочисленный объект не создается из нуля, поэтому нет никаких причин для NumberFormatException.null
(который не является примитивным числовым типом), применимое предложение будет следующим: «Если p является значением любого другого типа, преобразование в бокс эквивалентно преобразованию идентичности ». Таким образом, бокс преобразованиеnull
вInteger
выходыnull
, без вызова какого-либоInteger
конструктора.Я думаю, компилятор Java интерпретирует
true ? null : 0
какInteger
выражение, которое можно неявно преобразовать вint
, возможно, датьNullPointerException
.Во втором случае выражение
null
имеет специальный нулевой тип see , поэтому кодreturn null
делает несоответствие типов.источник
true ? null : 0
какInteger
? По автобоксу0
сначала ??На самом деле, все это объясняется в спецификации языка Java .
Поэтому "null" в вашем
(true ? null : 0)
получает тип int, а затем автоматически упаковывается в Integer.Попробуйте что-то подобное, чтобы убедиться в этом,
(true ? null : null)
и вы получите ошибку компилятора.источник
int
значение из функции, которая вызывает NPE.null
чтобыInteger
сnew Integer(null);
«Пусть T1 тип , который является результатом применения преобразования по боксу S1 ...» вы получитеNumberFormatException
и это не так ...В случае
if
оператораnull
ссылка не рассматривается какInteger
ссылка, потому что она не участвует в выражении, которое заставляет его интерпретироваться как таковое. Поэтому ошибка может быть легко обнаружена во время компиляции, потому что это более четко ошибка типа .Что касается условного оператора, спецификация языка Java §15.25 «Условный оператор
? :
» хорошо отвечает на это в правилах о том, как применяется преобразование типов:источник
0
он автоматически установлен,Integer
то компилятор выполняет последний случай «правил троичного оператора», как описано в Спецификации языка Java. Если это правда, тогда мне трудно поверить, что тогда он перейдет к случаю 3 тех же правил, имеющих нулевой и ссылочный тип, которые делают возвращаемое значение троичного оператора ссылочным типом (целое число). .Integer
? Это именно то, что происходит; NPE генерируется при попытке распаковать значение выражения для возвратаint
из функции. Измените функцию, чтобы она возвращала,Integer
и она вернетсяnull
без проблем.null
попадает в эту категорию , Кроме того, мы затем перейдем к шагу «В противном случае применяется двоичное числовое продвижение (§5.6.2) ... Обратите внимание, что двоичное числовое продвижение выполняет преобразование без распаковки (§5.1.8) ...», чтобы определить тип возвращаемого значения. Но преобразование без коробки генерирует NPE, и это происходит только во время выполнения, а не при попытке определить тип троичного оператора. Я все еще в замешательстве ..null
Рассматривается , как если бы это был типint
, но на самом деле эквивалентноthrow new NullPointerException()
, что все.Первое, что нужно иметь в виду, это то, что троичные операторы Java имеют «тип», и именно это компилятор будет определять и учитывать независимо от того, что является действительным / реальным типом второго или третьего параметра. В зависимости от нескольких факторов тип троичного оператора определяется по-разному, как показано в спецификации языка Java 15.26.
В приведенном выше вопросе мы должны рассмотреть последний случай:
Это, безусловно, самый сложный случай, когда вы смотрите на применение преобразования захвата (§5.1.10) и больше всего на lub (T1, T2) .
Простым английским языком и после крайнего упрощения мы можем описать процесс как вычисление «Наименьшего общего суперкласса» (да, подумайте о LCM) второго и третьего параметров. Это даст нам троичный оператор «тип». Опять же, то, что я только что сказал, является крайним упрощением (рассмотрим классы, которые реализуют несколько общих интерфейсов).
Например, если вы попробуете следующее:
Вы заметите, что результирующий тип условного выражения -
java.util.Date
это «Наименьший общий суперкласс» для парыTimestamp
/Time
.Так как
null
может быть автоматически помещен во что угодно, «Наименьший общий суперкласс» являетсяInteger
классом, и это будет тип возврата условного выражения (тернарный оператор) выше. Тогда возвращаемое значение будет нулевым указателем типаInteger
и именно это будет возвращено троичным оператором.Во время выполнения, когда виртуальная машина Java распаковывает , выбрасывается
Integer
aNullPointerException
. Это происходит потому, что JVM пытается вызвать функциюnull.intValue()
, гдеnull
есть результат автобоксирования.По моему мнению (и поскольку мое мнение не в спецификации языка Java, многие люди все равно сочтут его неправильным), компилятор плохо справляется с оценкой выражения в вашем вопросе. Учитывая, что вы написали,
true ? param1 : param2
компилятор должен сразу определить, чтоnull
будет возвращен первый параметр -, и это должно вызвать ошибку компилятора. Это похоже на то, когда вы пишете,while(true){} etc...
и компилятор жалуется на код под циклом и помечает его какUnreachable Statements
.Ваш второй случай довольно прост, и этот ответ уже слишком длинный ...;)
ИСПРАВЛЕНИЕ:
После другого анализа я считаю, что я был неправ, говоря, что
null
значение может быть упаковано / автоматически упаковано для чего угодно. Говоря о классе Integer, явный бокс состоит в вызовеnew Integer(...)
конструктора или, возможно,Integer.valueOf(int i);
(я где-то нашел эту версию). Первый бросил быNumberFormatException
(а этого не происходит), а второй просто не имел бы смысла, поскольку неint
может бытьnull
...источник
null
В исходном коде OP является не боксировал. Это работает так: компилятор предполагает, чтоnull
это ссылка на Integer. Используя правила для тернарных типов выражений, он решает, что все выражение является целочисленным выражением. Затем он генерирует код для автоматической блокировки1
(в случае, если условие оценивается какfalse
). Во время выполнения условие оцениваетсяtrue
как выражениеnull
. При попытке вернуть функциюint
из функцииnull
она распаковывается. Это тогда бросает NPE. (Компилятор может оптимизировать большую часть всего этого.)На самом деле, в первом случае выражение может быть оценено, поскольку компилятор знает, что оно должно быть оценено как a
Integer
, однако во втором случае тип возвращаемого значения (null
) не может быть определен, поэтому его нельзя скомпилировать. Если выInteger
приведете его к , код скомпилируется.источник
источник
Как насчет этого:
Вывод правда, правда.
Цвет Eclipse кодирует 1 в условном выражении как автоматически упакованный.
Я предполагаю, что компилятор видит возвращаемый тип выражения как Object.
источник