Java-тернарный оператор vs if/else в <совместимости JDK8

Недавно я читаю исходный код Spring Framework. Что-то, что я не могу понять, здесь:

public Member getMember() {
    // NOTE: no ternary expression to retain JDK <8 compatibility even when using
    // the JDK 8 compiler (potentially selecting java.lang.reflect.Executable
    // as common type, with that new base class not available on older JDKs)
    if (this.method != null) {
        return this.method;
    }
    else {
        return this.constructor;
    }
}

Этот метод является членом класса org.springframework.core.MethodParameter. Код легко понять, в то время как комментарии сложны.

ПРИМЕЧАНИЕ: нет тройного выражения для сохранения совместимости с JDK < 8 даже при использовании компилятора JDK 8 (возможность выбора java.lang.reflect.Executable как общего типа, причем этот новый базовый класс недоступен для старых JDK)

Какая разница между использованием тернарного выражения и использованием конструкции if...else... в этом контексте?

Ответ 1

Когда вы думаете о типе операндов, проблема становится более очевидной:

this.method != null ? this.method : this.constructor

имеет как тип наиболее специализированный общий тип обоих операндов, т.е. наиболее специализированный тип, общий как для this.method, так и this.constructor.

В Java 7 это java.lang.reflect.Member, однако библиотека классов Java 8 вводит новый тип java.lang.reflect.Executable, который более специализирован, чем общий Member. Следовательно, с библиотекой классов Java 8 тип результата тройного выражения Executable, а не Member.

Некоторые (предварительные) версии компилятора Java 8, по-видимому, дали явную ссылку на Executable внутри сгенерированного кода при компиляции тернарного оператора. Это вызовет загрузку класса и, следовательно, в свою очередь, ClassNotFoundException во время выполнения при запуске с библиотекой классов < JDK 8, поскольку Executable существует только для JDK ≥ 8.

Как отметил Тагир Валеев в этом ответе, это на самом деле ошибка в предварительных версиях JDK 8 и с тех пор исправлена, поэтому оба if-else обходной путь и пояснительный комментарий теперь устарели.

Примечание:. Можно прийти к выводу, что эта ошибка компилятора присутствовала до Java 8. Однако байт-код, сгенерированный для trernary OpenJDK 7, совпадает с байтовым кодом, сгенерированным OpenJDK 8. На самом деле, тип выражения полностью исключается во время выполнения, код действительно является только тестом, ветвью, нагрузкой, возвратом без каких-либо дополнительных проверок. Поэтому будьте уверены, что это не проблема (больше) и, по-видимому, временная проблема во время разработки Java 8.

Ответ 2

Это было представлено в довольно старой фиксации на 3 мая 2013 года почти за год до официального выпуска JDK-8. В те времена компилятор находился в тяжелом развитии, поэтому такие проблемы совместимости могут возникнуть. Думаю, команда Spring просто проверила сборку JDK-8 и попыталась исправить проблемы, хотя они и являются проблемами с компилятором. По официальному выпуску JDK-8 это стало неуместным. Теперь тернарный оператор в этом коде работает нормально, как и ожидалось (ссылка на класс Executable в компилированном .class файле отсутствует).

В JDK-9 аналогичные вещи появляются: некоторый код, который можно скомпилировать в JDK-8, не удалось с Jav-9 javac. Думаю, большинство таких проблем будут исправлены до релиза.

Ответ 3

Главное отличие состоит в том, что блок if else является выражением, тогда как тройной (чаще известный как условный оператор в Java) является выражение.

Оператор может выполнять функции типа return вызывающему на некоторых путях управления. Выражение может использоваться в присваивании:

int n = condition ? 3 : 2;

Итак, два выражения в тройном после условия должны быть коэрцитируемыми для одного и того же типа. Это может вызвать некоторые нечетные эффекты в Java, в частности, с автоматическим боксом и автоматическим литье ссылок - это то, о чем идет комментарий в вашем опубликованном коде. Принуждение выражений в вашем случае относится к типу java.lang.reflect.Executable (как к самому специализированному типу) и не существует в более старых версиях Java.

Стилистически вы должны использовать блок if else, если код является похожим на инструкцию, и тройным, если он похож на выражение.

Конечно, вы можете сделать блок if else таким же, как выражение, если вы используете лямбда-функцию.

Ответ 4

Тип возвращаемого значения в тернарном выражении зависит от родительских классов, которые изменились, как описано в Java 8.

Трудно понять, почему бросок не мог быть написан.