Почему тернарный оператор неожиданно приводит целые числа?

Я видел, как где-то обсуждалось, что следующий код приводит к тому, что obj является Double, но печатает 200.0 с левой стороны.

Object obj = true ? new Integer(200) : new Double(0.0);

System.out.println(obj);

Результат: 200,0


Однако, если вы поместите другой объект справа, например. BigDecimal, тип obj это Integer как и должно быть.

Object obj = true ? new Integer(200) : new BigDecimal(0.0);

System.out.println(obj);

Результат: 200


Я предполагаю, что причина этого как-то связана с приведением левой части к double таким же образом, как это происходит для integer/double сравнений и вычислений, но здесь левая и правая стороны не взаимодействуют таким образом.

Почему это происходит?


person HXCaine    schedule 03.11.2011    source источник
comment
Я не разбираюсь в Java, но правильный вопрос: каков тип тернарного условного выражения?   -  person Kerrek SB    schedule 04.11.2011
comment
Должен признать, что это довольно запутанный материал. Меня очень удивило, когда я запустил это в NetBeans, и obj.getClass() показывает Double. Какого черта, чувак.   -  person G_H    schedule 04.11.2011


Ответы (3)


Вам необходимо прочитать раздел 15.25 Спецификации языка Java.

Особенно:

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

  • Если один из операндов имеет тип byte или Byte, а другой — тип short или Short, то условное выражение имеет тип short.
  • Если один из операндов имеет тип T, где T — byte, short или char, а другой операнд — константное выражение типа int, значение которого может быть представлено в типе T, то › — тип условного выражения — T.
  • Если один из операндов имеет тип Byte, а другой операнд является константным выражением типа int, значение которого может быть представлено в типе byte, то тип условного выражения — byte.
  • Если один из операндов имеет тип Short, а другой операнд является константным выражением типа int, значение которого может быть представлено в виде типа short, то тип условного выражения — short.
  • Если один из операндов имеет тип; Символ и другой операнд — это константное выражение типа int, значение которого может быть представлено в типе char, тогда тип условного выражения — char.
  • В противном случае к типам операндов применяется двоичное числовое расширение (§5.6.2), а тип условного выражения является расширенным типом второго и третьего операндов. Обратите внимание, что двоичное числовое продвижение выполняет преобразование распаковки (§5.1.8) и преобразование набора значений (§5.1.13).

Таким образом, применяется двоичное числовое продвижение, которое начинается с:

Когда оператор применяет двоичное числовое преобразование к паре операндов, каждый из которых должен обозначать значение, которое может быть преобразовано в числовой тип, применяются следующие правила в порядке использования расширяющего преобразования (§5.1.2) для преобразования операндов по мере необходимости. :

  • Если какой-либо из операндов имеет ссылочный тип, выполняется преобразование распаковки (§5.1.8). Затем:
  • Если один из операндов имеет тип double, другой преобразуется в double.

Именно это и происходит здесь — типы параметров преобразуются в int и double соответственно, второй операнд (третий в исходном выражении) имеет тип double, поэтому общий тип результата — double.

person Jon Skeet    schedule 03.11.2011
comment
Гах Джон Скит опередил меня :) +1 - person Vivin Paliath; 04.11.2011
comment
@VivinPaliath Да, я тоже, но это нормально, потому что я как раз собирался украсть его цитату из другого вопроса о тернарном операторе, который у нас был некоторое время назад ;-) - person Voo; 04.11.2011
comment
Хотя я рад, что на него есть документация, я нигде не вижу причин для этого (но я уверен, что не ищу должным образом). Мне кажется, что это присваивание и нет необходимости в двоичном числовом продвижении, я что-то упускаю? - person HXCaine; 04.11.2011
comment
То же самое здесь... Я немного сбит с толку, почему это считается необходимым. Это может быть полезно с чисто функциональной точки зрения, но с использованием рефлексии и множества проверок Class в некоторых случаях это кажется огромной ошибкой. - person G_H; 04.11.2011
comment
@HXCaine: Что бы вы хотели, чтобы это делало в случае тех же типов, но примитивных? Имейте в виду, что тип выражения отделен от части присваивания. - person Jon Skeet; 04.11.2011
comment
Ага, понятно. Итак, синтаксически условное выражение должно иметь собирательный тип для анализа/компиляции еще до того, как условие будет оценено? - person HXCaine; 04.11.2011
comment
@HXCaine: Да. Компилятор решает, что это за тип, а затем рассматривает, можно ли его преобразовать в цель присваивания. Обратите внимание, что вывод универсального типа является одним из контрпримеров, но это единственный, который я могу придумать навскидку :) - person Jon Skeet; 04.11.2011

Числовое преобразование в условном операторе ? :

В условном операторе a?b:c, если и b, и c являются разными числовыми типами, во время компиляции применяются следующие правила преобразования, чтобы сделать их типы равными, по порядку:

  • Типы преобразуются в соответствующие им примитивные типы, что называется распаковкой.

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

  • В противном случае меньший тип преобразуется в следующий больший, пока оба операнда не будут иметь одинаковый тип. Порядок преобразования:
    byte -> short -> int -> long -> float -> double
    char -> int -> long -> float -> double

В конце концов все условное выражение получает тип второго и третьего операндов.

Примеры:
Если вы объедините char с short, выражение станет int.
Если вы объедините Integer с Integer, выражение станет Integer.
Если вы объедините final int i = 5 с Character, выражение станет char.
> Если вы объедините short с float, выражение станет float.

В примере вопроса 200 преобразуется из Integer в double, 0.0 распаковывается из Double в double, и все условное выражение становится double, которое в конечном итоге упаковывается в Double, потому что obj имеет тип Object.

person Gerhard    schedule 16.03.2017
comment
Цель этого ответа — дать полное определение, включая распаковку, числовое продвижение и т. д., с помощью нескольких простых правил, которые легко запомнить. - person Gerhard; 16.03.2017

Пример:

public static void main(String[] args) {
    int i = 10;
    int i2 = 10;
    long l = 100;
    byte b = 10;
    char c = 'A';
    Long result;
    // combine int with int result is int compiler error
   // result = true ? i : i2; // combine int with int, the expression becomes int
    //result = true ? b : c; // combine byte with char, the expression becomes int

    //combine int with long result will be long
    result = true ? l : i; // success - > combine long with int, the expression becomes long
    result = true ? i : l; // success - > combine int with long, the expression becomes long
    result = true ? b : l; // success - >  combine byte with long, the expression becomes long
    result = true ? c : l; // success - >  char long with long, the expression becomes long

    Integer intResult;
    intResult = true ? b : c; // combine char with byte, the expression becomes int.
   // intResult = true ? l : c; // fail combine long with char, the expression becomes long.

}
person ikarayel    schedule 02.01.2019