Почему компилятор Java допускает приведение null к примитивному типу в тернарном операторе

Эта маленькая программа бросает NullPointerException на линии троичного оператора:

public class Main {
    int someMethod() {
        return (true ? null : 0);
    }

    public static void main(String[] args)  {
        Main obj= new Main();
        obj.someMethod();
    }
}

Я понимаю причина в null не может быть приведен к int,

Однако вопрос заключается в том, почему компилятор Java допускает передачу такого рода кода, в то время как что-то подобное ниже приведет к ошибке времени компиляции:

int i = null; //Error: incompatible types: <nulltype> cannot be converted to int

2 ответа

Решение

Согласно спецификации языка Java - условный оператор, Java будет оценивать условное выражение во время выполнения, а не во время компиляции. Вот почему ошибка не обнаружена во время компиляции:

Во время выполнения первое выражение операнда условного выражения вычисляется первым. Полученное логическое значение затем используется для выбора второго или третьего выражения операнда.

Итак, в вашем случае:

int someMethod() {
    return (true ? null : 0);
}

изображений true является методом, содержащим сложную логику, и имеет смысл, если Java вычисляет 1-й операнд (в данном случае true) во время выполнения. Затем на основании правила:

Если один из второго и третьего операндов имеет примитивный тип T, а тип другого является результатом применения преобразования в бокс (§5.1.7) к T, то тип условного выражения - T.

С 3-го операнда 0 тип примитива (T), тип выражения будет тип T (int). Итак, распаковка нулевой ссылки на int приведет к NPE.

Компилятор запрещает

int i = null;

потому что тип выражения null является "нулевым типом", и для "нулевого типа" не существует правила преобразования распаковки. ( JLS раздел 5.1.8).

Тем не менее, разрешено писать:

int i = (Integer) null;

потому что выражение (Integer) null имеет тип java.lang.Integer и вызовет распаковку, что всегда приводит к исключению NullPointerException.

Когда вы пишете:

return (true ? null : 0);

в методе, который возвращает int или просто когда пишешь:

int i = (true ? null : 0);

это компилируется. Тип (true ? null : 0) является java.lang.Integer и это становится похожим на int i = (Integer) null;

Вы должны отметить, что null может быть "упакован" как Integer, Float, Double и т. д. в контексте троичного выражения (a ? b : c). Это конкретно описано в JLS 5.1.7 (преобразования в боксы) и JLS 15.25 (условный оператор "?:").

Другие вопросы по тегам