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

Тернарное выражение иногда обходит проверки проверки компилятора

По какой-то причине следующий код обычно компилируется:

public double getSomeDouble() {
    return "" != null ? 3.7d : null;
}

Обычно я ожидал бы Eclipse, чтобы пометить его как ошибку (null нельзя преобразовать в примитив double).

Как раз для моего предположения, этот код не будет работать:

public double getSomeDouble() {
    return null;
}

Eclipse отметит строку return null как ошибку, указав:

Несоответствие типов: невозможно преобразовать из null в double

Почему он не говорит то же самое в предыдущем фрагменте кода?!

4b9b3361

Ответ 1

Из-за autoboxing и autounboxing. Если вы посмотрите на байт-код (см. Ниже), вы можете увидеть вызовы Double.valueOf (бокс 3.7d) и Double#doubleValue (unboxing результат условного выражения). Операнды к условному оператору должны быть одного типа, поэтому компилятор эффективно превращает ваш код в это:

public double getSomeDouble() {
    return ("" != null ? Double.valueOf(3.7d) : null).doubleValue();
}

... потому что Double - наиболее специфический общий тип, который он может найти для 3.7d и null.

Я использовал строковый аргумент (чтобы исключить оптимизацию компилятора вокруг инвариантного выражения "" != null, который компилятор мог бы сказать, никогда не будет правдой):

public double getSomeDouble(String str) {
     return str != null ? 3.7d : null;
}

который эффективно становится:

public double getSomeDouble(String str) {
    return (str != null ? Double.valueOf(3.7d) : null).doubleValue();
}

... и действительно получил NPE во время выполнения, когда я прошел в null для str, когда он попытался вызвать doubleValue() на null.

Здесь байт-код для моего getSomeDouble(String) (от javap -c MyClass):

  public double getSomeDouble(java.lang.String);
    Code:
       0: aload_1
       1: ifnull        13
       4: ldc2_w        #7                  // double 3.7d
       7: invokestatic  #9                  // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
      10: goto          14
      13: aconst_null
      14: invokevirtual #10                 // Method java/lang/Double.doubleValue:()D
      17: dreturn