Почему компилятор Java не создает недопустимую ошибку оператора для недостижимого оператора?
Если я попытаюсь скомпилировать
for(;;)
{
}
System.out.println("End");
В компиляторе Java появляется сообщение об ошибке Unreachable statement
. Но если я добавлю еще один "недостижимый" (по мне) оператор break
и сделаю это:
for(;;)
{
if(false) break;
}
System.out.println("End");
Он компилируется. Почему это не приводит к ошибке? Является ли Java попыткой сказать, что
Две ошибки сделать сделать правильно?
Ответы
Ответ 1
Поведение определено в описании JLS недостижимых утверждений:
Оператор then доступен, если утверждение if-then доступно.
Итак, компилятор определяет, что оператор then (break;
) доступен, независимо от условия в if
.
И немного дальше, мой удар:
Базовый оператор for
может завершиться нормально, если по крайней мере одно из следующего верно:
- Оператор for доступен, существует выражение условия, а выражение условия не является константным выражением (§15.28) со значением true.
- Существует допустимый оператор
break
, который завершает оператор for.
Таким образом, for может завершиться нормально, потому что then-statement содержит a break
. Как вы заметили, это не сработает, если вы замените break
на return
.
Объяснение объясняется в конце раздела. По существу, if
имеет специальную обработку, позволяющую конструкции, такие как:
if(DEBUG) { ... }
где DEBUG может быть константой времени компиляции.
Ответ 2
Как объяснено в моем ответе на аналогичный вопрос, конкретный конструктор if(compile-time-false)
освобождается от правил недостижимости как явный бэкдор. В этом случае компилятор рассматривает ваш break
как достижимый из-за этого.
Ответ 3
Из JLS
Операция if-then может завершиться нормально, если хотя бы один из верно следующее:
Оператор if-then доступен и выражение условия не является постоянное выражение, значение которого истинно.
> Оператор then может завершиться нормально.
Значит if(false)
.
Эта способность "условно компилировать" оказывает значительное влияние, и отношение к бинарной совместимости. Если набор классов которые используют такую переменную "флаг", компилируются, а условный код пропущено, позже не будет достаточно распространять только новую версию класс или интерфейс, который содержит определение флага. изменение значения флага, следовательно, не совместимо с бинарными с уже существующими двоичными файлами. (Есть и другие причины для такая несовместимость, как, например, использование констант в случае метки в операторах switch;)
Ответ 4
В принципе, недостижимый код обнаруживается путем анализа программы статически без, фактически выполняющей код. Пока условие будет проверено во время выполнения. Таким образом, когда этот анализ имеет место, он фактически не смотрит в состояние, а просто проверяет, что break;
доступен (доступен) через if
.
Ответ 5
Основная причина, по которой Java не обнаруживает все недостижимые утверждения, состоит в том, что вообще невозможно ответить, доступен ли код или нет. Это следует из того факта, что проблема с остановкой неразрешима над машинами Тьюринга.
Итак, ясно, что все недостижимые утверждения не могут быть обнаружены, но почему бы не попробовать оценить условия? Представьте себе, что используемое условие не просто false
, а нечто вроде ~x == x
. Например, все эти операторы будут печатать true
для каждого int x
(источника).
System.out.println((x + x & 1) == 0);
System.out.println((x + -x & 1) == 0);
System.out.println((-x & 1) == (x & 1));
System.out.println(((-x ^ x) & 1) == 0);
System.out.println((x * 0x80 & 0x56) == 0);
System.out.println((x << 1 ^ 0x1765) != 0);
Операции могут быть довольно сложными; для их решения требуется время. Это значительно увеличит время сборки, и в конце концов, оно не обнаружит все недостижимые утверждения. Компилятор был разработан, чтобы приложить некоторые усилия, но не тратить слишком много времени на это.
Остается только вопрос: где прекратить разрешать условия? Причины этого не имеют математического обоснования и основаны на сценарии использования. Обоснование для вашего конкретного случая дается JLS-14.21