Отменено, если условие в байт-коде Java
Рассмотрим простой пример
private static String isPositive(int val) {
if (val > 0) {
return "yes";
} else {
return "no";
}
}
Здесь все довольно просто: если val > 0
вернуть yes
еще вернуться no
, Но после компиляции, в байт-коде, это если условие полностью изменено:
private static isPositive(I)Ljava/lang/String;
L0
LINENUMBER 12 L0
ILOAD 0
IFLE L1
L2
LINENUMBER 13 L2
LDC "yes"
ARETURN
L1
LINENUMBER 15 L1
FRAME SAME
LDC "no"
ARETURN
Проверяет: если val <= 0
затем вернитесь no
, иначе возврат yes
,
Сначала я подумал, что <=
чек дешевле, а это какая-то оптимизация. Но если я изменю свой исходный код на
if (val <= 0) {
return "no";
} else {
return "yes";
}
все еще будет обращено в байт-код:
L0
LINENUMBER 12 L0
ILOAD 0
IFGT L1
L2
LINENUMBER 13 L2
LDC "no"
ARETURN
L1
LINENUMBER 15 L1
FRAME SAME
LDC "yes"
ARETURN
Итак, есть ли причина для такого поведения? Можно ли изменить это на прямое?
3 ответа
Вероятно, это делается так, чтобы два блока кода в if
отображаются в том же порядке в переведенном байт-коде.
Например, этот код Java:
if (val > 0) {
return "yes";
} else {
return "no";
}
Переводит что-то вроде этого (псевдокод):
If val <= 0, then branch to L1
return "yes"
L1:
return "no"
Обратите внимание, что в исходном коде Java if
проверяется условие, чтобы увидеть, должен ли выполняться первый блок кода, в то время как в переведенном байт-коде выполняется проверка, чтобы определить, должна ли быть выполнена ветвь (пропуская первый блок кода). Поэтому необходимо проверить дополнительное условие.
Можно ли изменить это на прямое?
Конечно, было бы также возможно сохранить условие, но тогда вам нужно будет изменить порядок двух кодовых блоков:
If val > 0, then branch to L1
return "no"
L1:
return "yes"
Я бы не сказал, что эта версия "более прямолинейна", чем предыдущая.
В любом случае, почему вы хотите изменить это? Обе версии должны быть в порядке.
На самом деле, изменение условия является самой простой стратегией компиляции. Вы пишете код Java, соответствующий шаблону
if(condition_fullfilled) {
somecode
}
который будет скомпилирован в байт-код, соответствующий шаблону
goto_if_condition_not_fullfilled A_LABEL
compiled_somecode
A_LABEL:
Так как условная ветвь говорит, когда пропустить условный код, его условие должно быть противоположным вашему исходному коду, когда нужно выполнить условный код.
Пример выше, не имеющий else
часть, демонстрирует, почему нет простого способа компиляции if
с условной инструкцией ветвления, имеющей то же условие, что и ваш исходный код. Это возможно, но потребует более одной инструкции ветвления.
Это простая стратегия компиляции для if
заявления без else
может легко расширяться для обработки else
также. Нет причин менять стратегию, например, менять порядок операторов, когда else
пункт присутствует.
Обратите внимание, что в вашем случае, когда обе ветви заканчиваются return
заявление, нет никакой разницы между
if (val > 0) {
return "yes";
} else {
return "no";
}
а также
if (val > 0) {
return "yes";
}
return "no";
тем не мение.
Возможно, причина в стеке операндов, который использует байт-код для выполнения своих операторов.
Сначала выполняется сравнение и 1, 0 или -1 помещается в стек операндов. Затем выполняется ветвление в зависимости от того, больше ли значение в стеке операндов, меньше или равно нулю.
В этом есть хорошая графическая деталь, приведенная в условных обозначениях JavaCodeToByteCode#, а также подробная инструкция по ветвлению.