Объект кода o = true? new Integer (0): new Long (1) возвращает Long со значением 0. Почему?
Пожалуйста, подумайте, что у нас есть код ниже:
Object obj = true ? new Integer(0) : new Long(1);
System.out.println(obj.getClass() + "\nvalue = " + obj);
И его результат:
class java.lang.Long
value = 0
Вместо:
class java.lang.Integer
value = 0
Может ли кто-нибудь уточнить, почему у нас есть такая функциональность на Java? Это очень странно для меня.
Есть ли у вас пример, где это может быть полезно?
UPDATE:
Вот фрагмент байт-кода, где мы можем видеть, что происходит там
NEW java/lang/Integer
DUP
LDC "0"
INVOKESPECIAL java/lang/Integer.<init> (Ljava/lang/String;)V
INVOKEVIRTUAL java/lang/Integer.intValue ()I
I2L
INVOKESTATIC java/lang/Long.valueOf (J)Ljava/lang/Long;
ASTORE 1
Ответы
Ответ 1
Здесь происходит результат
- Двоичное числовое продвижение, превращающее типы
Integer
и Long
в Long
для использования в качестве общего типа для применения к условному выражению оператора
- Unboxing те объекты оболочки
- И затем бокс результирующего значения условного выражения
Условный оператор второго и третьего операндов должен иметь тот же тип, который является результирующим типом выражения. Integer
и Long
не являются, конечно, одним и тем же типом.
Однако, как описано в JLS§15.25, компилятор применит двоичное числовое продвижение при определении возможного общего типа для применения к выражению. Этот раздел имеет удобную таблицу 15.25-D, которая говорит нам, что, когда второй операнд имеет тип Integer
, а третий операнд имеет тип Long
, компилятор будет выполнять двоичное числовое продвижение по Integer,Long
. Двоичное числовое продвижение по Integer,Long
дает Long
. Таким образом, результат условного выражения оператора Long
.
Так как тип результата выражения Long
, Integer
или Long
должен быть распакован (а затем брошен, в случае Integer
).
Наконец, вы назначаете его Object
, который заставляет бокс и обертывает Long
в Long
. Таким образом, вы получаете Long
, содержащее значение 0
, которое соответствует вашему результату.
Таким образом, если мы проигнорируем тот факт, что компилятор оптимизирует половину следующих действий, поскольку он имеет дело с постоянным выражением, благодаря true
в коде (я использовал вместо него flag
), этот код заканчивается тем, что:
Object obj = Long.valueOf(flag ? (long)(new Integer(0)).intValue() : (new Long(1)).longValue());
System.out.println(obj.getClass() + "\nvalue = " + obj);
-
(long)(new Integer(0)).intValue()
представляет unboxing Integer
и отбрасывает его на Long
, чтобы он соответствовал типу результата выражения.
-
(new Long(1)).longValue()
представляет unboxing Long
, чтобы он соответствовал типу результата выражения.
- И
Long.valueOf
представляет бокс в конце.
Ответ 2
Это поведение хорошо объяснено в JLS - 15.25. Условный оператор?::
Условный оператор имеет три выражения операнда. ?
появляется между первым и вторым выражениями, а :
появляется между вторым и третьим выражениями.
[...]
Тип условного выражения определяется следующим образом:
-
[...]
-
В противном случае, если второй и третий операнды имеют типы, которые конвертируются (§5.1.8) в числовые типы, тогда существует несколько случаев:
Ответ 3
На самом деле long может хранить значение целого числа, но целое число не может хранить значение long (концепция расширяется), и вы сохраняете его в Object, чтобы он хранил его в Long. внутренне он также использует бокс и распаковку
Возможный код компилятора:
Long obj = true ? new Integer(0) : new Long(1);
System.out.println(obj.getClass() + "\nvalue =" + obj);
Не работает код (не удается скомпилировать):
Integer obj = true ? new Integer(0) : new Long(1);
System.out.println(obj.getClass() + "\nvalue =" + obj);
проверьте сами и сообщите мне, если у вас есть какие-либо сомнения в этом.
Ответ 4
Это связано с тем, как работает тернарный оператор.
Операнды с обеих сторон :
должны быть совместимых типов, поэтому вы не можете этого сделать:
true ? 1 : "Hello"
Так как int
и String
не могут быть принудительно преобразованы друг в друга.
В вашем случае, однако, типы совместимы! int
может быть неявно преобразован в long
. И это то, что сделал компилятор! Он видел int и долго и решает, что выражение должно оцениваться до long
. Он отключает два значения и принудительно преобразует int
в long
. И, наконец, он помещает результирующий long
, поэтому он становится long
и помещает его в переменную.