"Неверный" тип возврата при использовании if vs. trernary opertator в Java - программирование
Подтвердить что ты не робот

"Неверный" тип возврата при использовании if vs. trernary opertator в Java

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

return condition?a:b;

эквивалентно

if(condition) {
    return a;
} else{ 
    return b;
}

Первый возвращает двойной, а второй - длинный:

public class IfTest {
    public static Long longValue = 1l;
    public static Double doubleValue = null;

    public static void main(String[] args) {
        System.out.println(getWithIf().getClass());// outpus Long
        System.out.println(getWithQuestionMark().getClass());// outputs Double
    }

    public static Object getWithQuestionMark() {
        return doubleValue == null ? longValue : doubleValue;
    }

    public static Object getWithIf() {
        if (doubleValue == null) {
            return longValue;
         } else {
            return doubleValue;
        }
    }
}

Я могу себе представить, что это связано с тем, что компилятор ограничен, возвращая тип возвращаемого значения getWithQuestionMark(), но насколько этот язык подходит? Это, конечно, не то, что я ожидал.

Любые идеи наиболее приветствуются!

Редактировать: там очень хорошие ответы ниже. Кроме того, следующий вопрос, на который ссылается @sakthisundar, исследует другой побочный эффект продвижения типа, возникающий у тройного оператора: Tricky trernary operator в Java - автобоксинг

4b9b3361

Ответ 1

В основном это соответствует правилам раздела 15.25 JLS, в частности:

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

  • [...]

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

Итак, раздел 5.6.2, который будет в основном связан с распаковкой, поэтому это выражение работает как longValue и doubleValue имели типы long и double соответственно, а расширяющаяся акция применяется к long, чтобы получить общий тип результата double.

Затем double помещается в бокс, чтобы вернуть метод Object из этого метода.

Ответ 2

В дополнение к ответу @Jon, глядя на байт-код, вы видите:

public static java.lang.Object getWithQuestionMark();
  Code:
   0:   getstatic       #7; //Field doubleValue:Ljava/lang/Double;
   3:   ifnonnull       16
   6:   getstatic       #8; //Field longValue:Ljava/lang/Long;
   9:   invokevirtual   #9; //Method java/lang/Long.longValue:()J
   12:  l2d
   13:  goto    22
   16:  getstatic       #7; //Field doubleValue:Ljava/lang/Double;
   19:  invokevirtual   #10; //Method java/lang/Double.doubleValue:()D
   22:  invokestatic    #11; //Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
   25:  astore_0
   26:  aload_0
   27:  areturn

Если вы сообщите компилятору, что вас не интересуют цифры:

public static Object getWithQuestionMark() {
    return doubleValue == null ? (Object)longValue : (Object)doubleValue;
}

вы получите то, что было после (байт-код)

public static java.lang.Object getWithQuestionMark();
  Code:
   0:   getstatic       #7; //Field doubleValue:Ljava/lang/Double;
   3:   ifnonnull       12
   6:   getstatic       #8; //Field longValue:Ljava/lang/Long;
   9:   goto    15
   12:  getstatic       #7; //Field doubleValue:Ljava/lang/Double;
   15:  areturn

выходы:

$ java IfTest
class java.lang.Long
class java.lang.Long