Хитрый тернарный оператор в Java-autoboxing



давайте посмотрим на простой код 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() метод), который тут выдать ошибку времени компиляции! Зачем?

833   8  

8 ответов:

компилятор интерпретирует null как нулевая ссылка на Integer, применяет правила автобокса / распаковки для условного оператора (как описано в Спецификация Языка Java, 15.25), и радостно идет дальше. Это создаст NullPointerException во время выполнения, что вы можете подтвердить, попробовав его.

Я думаю, компилятор Java интерпретирует true ? null : 0 Как Integer выражение, которое может быть неявно преобразовано в int, возможно, давая NullPointerException.

для второго случая, выражение null особый тип nullпосмотреть, код return null делает несоответствие типов.

на самом деле, все объясняется в Спецификация Языка Java.

тип условного выражения определяется следующим образом:

  • если второй и третий операнды имеют одинаковый тип (который может быть нулевым тип), тогда тип условного выражения.

поэтому "null" в вашем (true ? null : 0) получает тип int, а затем автобокс в Integer.

попробовать что-то вроде этого, чтобы проверить это (true ? null : null) и вы получите ошибку компилятора.

в случае if заявление null ссылка не рассматривается как Integer ссылка, потому что он не участвует в выражение что заставляет его интерпретироваться как таковой. Поэтому ошибка может быть легко пойман во время компиляции, потому что это более явно тип ошибка.

что касается условного оператора, спецификация языка Java §15.25 " условный оператор ? : " отвечает на это красиво в правилах для как применяется преобразование типов:

  • если второй и третий операнды имеют один и тот же тип (который может быть null type), то это тип условного выражения.

    не применяется, потому что null не int.

  • если один из второго и третьего операндов имеет тип boolean и тип другое имеет тип Boolean, то тип условного выражения является логический.

    не применяется, потому что ни null, ни int и boolean или Boolean.

  • если один из второго и третьего операндов имеет нулевой тип и тип другое-это ссылочный тип, тогда тип условного выражения таков ссылочный тип.

    не применяется, потому что null имеет нулевой тип, но int не является ссылочным типом.

  • в противном случае, если второй и третий операнды имеют типы, которые могут быть конвертированы (§5.1.8) для числовых типов, то есть несколько случаев: [...]

    применяется: null рассматривается как конвертируемый в числовой тип и определяется в §5.1.8 "распаковка преобразования", чтобы бросить NullPointerException.

первое, что нужно иметь в виду, это то, что тернарные операторы Java имеют "тип", и что это то, что компилятор определит и рассмотрит независимо от того, каковы фактические/реальные типы второго или третьего параметра. В зависимости от нескольких факторов тип тернарного оператора определяется по-разному, как показано в Спецификация Языка Java 15.26

на вопрос, мы должны рассмотреть последний случай:

в противном случае, второй и третий операнды имеют типа S1 и S2 соответственно. Пусть T1 быть типом, который является результатом применения преобразования бокса в S1, и пусть T2 быть типом, который является результатом применения преобразования бокса в S2. Тип условного выражения является результатом применения преобразования захвата (§5.1.10) к lub (T1, T2) (§15.12.2.7).

это самый сложный случай, как только вы взглянете на применение преобразования захвата (§5.1.10) а в lub (T1, T2).

на простом английском языке и после крайнего упрощения мы можем описать процесс как вычисление "наименее распространенного суперкласса" (да, подумайте о LCM) второго и третьего параметров. Это даст нам тернарный оператор "тип". Опять же, что я просто это крайнее упрощение (рассмотрим классы, реализующие несколько общих интерфейсов).

например, если вы попробуете следующее:

long millis = System.currentTimeMillis();
return(true ? new java.sql.Timestamp(millis) : new java.sql.Time(millis));

Вы заметите, что результирующий тип условного выражения является java.util.Date так как это" наименее распространенный суперкласс " для Timestamp/Time пара.

С null может быть autoboxed к чему угодно, "наименее распространенный суперкласс" является Integer класс и это будет возвращаемый тип условное выражение (тернарный оператор) выше. Возвращаемое значение будет нулевым указателем типа Integer и это то, что будет возвращено тернарного оператора.

во время выполнения, когда виртуальная машина Java распаковывает Integer a NullPointerException бросается. Это происходит потому, что JVM пытается вызвать функцию null.intValue(), где null результат autoboxing.

на мой взгляд (и так как мое мнение не находится в спецификации языка Java многие люди в любом случае это будет неправильно) компилятор плохо работает при оценке выражения в вашем вопросе. Учитывая, что вы написали true ? param1 : param2 компилятор должен сразу определить, что первый параметр -null - будет возвращен, и он должен генерировать ошибку компилятора. Это несколько похоже на то, когда вы пишете while(true){} etc... и компилятор жалуется на код под циклом и помечает его с Unreachable Statements.

ваш второй случай довольно прост, и этот ответ уже слишком долго... ;)

устранение:

после другого анализа я считаю, что я был неправ, чтобы сказать, что a null значение может быть в штучной упаковке/autoboxed ни к чему. Говоря о классе Integer, явный бокс состоит в вызове new Integer(...) конструктор или, может быть,Integer.valueOf(int i); (я нашел эту версию где-то). Первый бросил бы NumberFormatException (и этого не происходит) в то время как второй просто не имеет смысла, так как int не может быть null...

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

private int temp() {

    if (true) {
        Integer x = null;
        return x;// since that is fine because of auto-boxing then the returned value could be null
        //in other words I can say x could be null or new Integer(intValue) or a intValue
    }

    return (true ? null : 0);  //this will be prefectly legal null would be refrence to Integer. The concept is one the returned
    //value can be Integer 
    // then null is accepted to be a variable (-refrence variable-) of Integer
}

Как насчет этого:

public class ConditionalExpressionType {

    public static void main(String[] args) {

        String s = "";
        s += (true ? 1 : "") instanceof Integer;
        System.out.println(s);

        String t = "";
        t += (!true ? 1 : "") instanceof String;
        System.out.println(t);

    }

}

вывод true, true.

цвет затмения кодирует 1 в условном выражении как автобокс.

Я предполагаю, что компилятор видит возвращаемый тип выражения как объект.

Comments

    Ничего не найдено.