Подтвердить что ты не робот

Странное поведение тернарного условного типа с ядром в Java

У меня была эта часть кода в моем приложении (упрощенная версия):

Object result;
if (check)
    result = new Integer(1);
else
    result = new Double(1.0);
System.out.println(result);
return result;

Затем я решил реорганизовать оператор if-else на тернарное условное выражение, чтобы мой код был более кратким:

Object result = check ? new Integer(1) : new Double(1.0);
System.out.println(result);
return result;

Оказалось, что в случае проверки true две версии печатают разные результаты:

1

или

1.0

Не тернарный условный эквивалент соответствующему if-else?

4b9b3361

Ответ 1

Значение if/else и условное (тройное) выражение не совсем эквивалентны. Результат условного выражения должен иметь тип.

Вы наблюдаете эффекты поощрения числового типа (или типа принуждения).

Здесь выдержка из спецификации языка (см. здесь), из раздела, описывающего возвращаемое значение условного выражения:

В противном случае, если второй и третий операнды имеют типы, которые являются конвертируемыми (п. 5.1.8), к числовым типам, то есть несколько случаев:

Последний такой случай (здесь я опустил другие случаи):

В противном случае для типов операндов применяется двоичное числовое продвижение (§5.6.2), а тип условного выражения - продвинутый тип второго и третьего операндов.

Здесь дополнительная спецификация, связанная с двоичной цифровой продвижением:

Расширение примитивного преобразования (§5.1.2) применяется для преобразования одного или обоих операндов, как указано в следующих правилах:

  • Если один из операндов имеет тип double, другой преобразуется в double.

Это первый случай (следующие случаи опущены). double всегда выигрывает.

Поэтому, независимо от порядка 2-го и 3-го операндов в условном выражении, возвращаемый тип выражения будет увеличен до double.

Ответ 2

Проще говоря, тернарный оператор не отличается от других операторов, в которых поощряется числовой тип.

Если у вас есть что-то вроде System.out.println(1 + 1.0), вы ожидаете, что оно напечатает 2.0, потому что операнды, используемые на выходе, подлежат промокодам с числовым типом.

Точно так же и с тернарным оператором: System.out.println(true ? 1 : 1.0) будет печатать 1.0 после выполнения тех же промокодов с числовым типом, которые он выполнил бы, если бы выражение было 1 + 1.0.

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

Ответ 3

Короткий ответ

Первый пример, явно введенный как Object, который вызывает повышение.

Второй пример неявно напечатан как Double, который вызывает числовое расширение.


Длинный ответ

В примере с Object отсутствует преобразование значений, только распечатка и печать 1.

Object result;
if (1 == 1)
    result = new Integer(1);
else
    result = new Double(1.0);

Если вы вместо этого используете Double, это будет расширение и печать 1.0.

Double result;
if (1 == 1)
    result = new Integer(1);
else
    result = new Double(1.0);

Это довольно просто, поскольку существует явный тип.

Тернарное выражение, однако, не имеет явного типа, а правила нетривиальны.

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

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

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

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

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

    • Если один из операндов имеет тип байта или байт, а другой имеет тип short или Short, то тип условного выражения является коротким.

    • Если один из операндов имеет тип T, где T является байтом, коротким или char, а другой операнд является константным выражением (§15.28) типа int, значение которого представляется в типе T, то тип условного выражения равен T.

    • Если один из операндов имеет тип T, где T - это Byte, Short или Character, а другой операнд является константным выражением (§15.28) типа int, значение которого представляется в типе U, является результатом применения преобразования unboxing в T, тогда тип условного выражения равен U.

    • В противном случае для типов операндов применяется двоичное числовое продвижение (§5.6.2), а тип условного выражения - продвинутый тип второго и третьего операндов. Обратите внимание, что двоичное числовое продвижение выполняет преобразование набора значений (§5.1.13) и может выполнять преобразование для распаковки (§5.1.8).

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

http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.25

"Повышенный тип" чисел Integer и Double равен Double.

Ответ 4

В дополнение к @pb2q answer

вы можете проверить его как

public class test {
    public static void main(String[] args) {
    Object result;
    Boolean check = true;

        if (check)
            result = new Integer(1);
        else
            result = new Double(1.0);
        System.out.println(result);
        result = check ? new Integer(2) : new Double(1.0);
        System.out.println(result);
    }
}

он будет печатать 2.0 вместо 2 из-за цифровой рекламы

Ответ 5

В дополнение к существующим ответам, вы можете избежать проблемы путем конкретного литья по типу, который вы хотите:

Object result = args.length < 100 ? (Object)2 : (Object)1.0;

Приведение в Object помещает целое число как целое, а double - в Double. Выражения по обе стороны от ":" являются тогда типом Object, поэтому компилятору не нужно создавать какие-либо дальнейшие преобразования.