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.
Трудно понять, почему бросок не мог быть написан.