Посмотрите на следующий бесконечный while
цикл в Java. Это вызывает ошибку времени компиляции для оператора под ним.
while(true) {
System.out.println("inside while");
}
System.out.println("while terminated"); //Unreachable statement - compiler-error.
Однако следующий тот же бесконечный while
цикл работает нормально и не вызывает ошибок, в которых я просто заменил условие логической переменной.
boolean b=true;
while(b) {
System.out.println("inside while");
}
System.out.println("while terminated"); //No error here.
Во втором случае также очевидно, что оператор после цикла недоступен, поскольку логическая переменная b
истинна, но компилятор вообще не жалуется. Почему?
Изменить: следующая версия while
застревает в бесконечном цикле как очевидное, но не выдает ошибок компилятора для оператора под ней, даже если if
условие внутри цикла всегда false
и, следовательно, цикл никогда не может вернуться и может быть определен компилятором в время самой компиляции.
while(true) {
if(false) {
break;
}
System.out.println("inside while");
}
System.out.println("while terminated"); //No error here.
while(true) {
if(false) { //if true then also
return; //Replacing return with break fixes the following error.
}
System.out.println("inside while");
}
System.out.println("while terminated"); //Compiler-error - unreachable statement.
while(true) {
if(true) {
System.out.println("inside if");
return;
}
System.out.println("inside while"); //No error here.
}
System.out.println("while terminated"); //Compiler-error - unreachable statement.
Изменить: то же самое с if
и while
.
if(false) {
System.out.println("inside if"); //No error here.
}
while(false) {
System.out.println("inside while");
// Compiler's complain - unreachable statement.
}
while(true) {
if(true) {
System.out.println("inside if");
break;
}
System.out.println("inside while"); //No error here.
}
Следующая версия while
также застревает в бесконечном цикле.
while(true) {
try {
System.out.println("inside while");
return; //Replacing return with break makes no difference here.
} finally {
continue;
}
}
Это связано с тем, что finally
блок всегда выполняется, даже если return
оператор встречается до него внутри самого try
блока.
Ответы:
Компилятор может легко и недвусмысленно доказать, что первое выражение всегда приводит к бесконечному циклу, но не так просто для второго. В вашем примере с игрушкой это просто, но что, если:
Компилятор явно не проверяет ваш более простой случай, потому что он полностью отказывается от этой дороги. Почему? Потому что это запрещено спецификацией
намного сложнее. См. Раздел 14.21 :(Кстати, мой компилятор делает жаловаться , когда переменная объявлена
final
.)источник
Согласно спецификациям , о операторах while сказано следующее.
Таким образом, компилятор скажет, что код, следующий за оператором while, недоступен, если условие while является константой с истинным значением или внутри while есть оператор break. Во втором случае, поскольку значение b не является константой, код, следующий за ним, не считается недоступным. По этой ссылке есть гораздо больше информации, чтобы дать вам более подробную информацию о том, что является и что не считается недоступным.
источник
Потому что истина постоянна и b можно изменить в цикле.
источник
Поскольку анализировать состояние переменной сложно, поэтому компилятор в значительной степени отказался и позволяет вам делать то, что вы хотите. Кроме того, в Спецификации языка Java есть четкие правила о том, как компилятору разрешено обнаруживать недоступный код .
Есть много способов обмануть компилятор - еще один распространенный пример:
public void test() { return; System.out.println("Hello"); }
что не сработает, так как компилятор поймет, что область недоступна. Вместо этого вы могли бы сделать
public void test() { if (2 > 1) return; System.out.println("Hello"); }
Это сработает, поскольку компилятор не может понять, что выражение никогда не будет ложным.
источник
if
немного отличается от использованияwhile
, потому что компилятор скомпилирует даже последовательностьif(true) return; System.out.println("Hello");
без жалоб. IIRC, это особое исключение в JLS. Компилятор сможет обнаружить недостижимый код послеif
так же просто, как сwhile
.Последнее не является недостижимым. Логическое значение b все еще может быть изменено на false где-нибудь внутри цикла, вызывая условие завершения.
источник
Я предполагаю, что переменная «b» имеет возможность изменить свое значение, так что компилятор думает, что это
System.out.println("while terminated");
возможно.источник
Компиляторы не идеальны - и не должны быть
Компилятор отвечает за подтверждение синтаксиса, а не за выполнение. В конечном итоге компиляторы могут уловить и предотвратить многие проблемы времени выполнения на строго типизированном языке, но они не могут уловить все такие ошибки.
Практическое решение состоит в том, чтобы иметь батареи модульных тестов в дополнение к проверкам компилятора ИЛИ использовать объектно-ориентированные компоненты для реализации логики, которая, как известно, является надежной, вместо того, чтобы полагаться на примитивные переменные и условия остановки.
Strong Typing и OO: повышение эффективности компилятора
Некоторые ошибки носят синтаксический характер, а в Java строгая типизация позволяет перехватить множество исключений во время выполнения. Но, используя лучшие типы, вы можете помочь вашему компилятору обеспечить лучшую логику.
Если вы хотите, чтобы компилятор более эффективно применял логику, в Java решение состоит в создании надежных необходимых объектов, которые могут обеспечивать выполнение такой логики, и использовании этих объектов для построения вашего приложения, а не примитивов.
Классическим примером этого является использование шаблона итератора в сочетании с циклом foreach в Java. Эта конструкция менее уязвима для типа ошибки, которую вы иллюстрируете, чем упрощенный цикл while.
источник
Компилятор недостаточно сложен для обработки значений, которые
b
могут содержать (хотя вы назначаете его только один раз). Первый пример компилятору легко увидеть, это будет бесконечный цикл, потому что условие не является переменным.источник
Я удивлен, что ваш компилятор отказался компилировать первый случай. Мне это кажется странным.
Но второй случай не оптимизирован для первого случая, потому что (а) другой поток может обновить значение
b
(б) вызываемая функция может изменить значениеb
в качестве побочного эффекта.источник
На самом деле, я не думаю, что кто-то понял это ПОЛНОСТЬЮ правильно (по крайней мере, в том смысле, в котором он изначально задавал вопрос). OQ продолжает упоминать:
Но это не имеет значения, потому что последняя строка ДОСТУПНА. Если вы взяли этот код, скомпилировали его в файл класса и передали файл класса кому-то другому (например, в качестве библиотеки), они могли бы связать скомпилированный класс с кодом, который изменяет «b» посредством отражения, выходя из цикла и вызывая последний строка для выполнения.
Это верно для любой переменной, которая не является константой (или final, которая компилируется в константу в том месте, где она используется - иногда вызывая странные ошибки, если вы перекомпилируете класс с final, а не с классом, который на него ссылается, ссылка класс по-прежнему будет хранить старое значение без каких-либо ошибок)
Я использовал возможность отражения для изменения неокончательных частных переменных другого класса, чтобы обезьяна исправляла класс в купленной библиотеке - исправляя ошибку, чтобы мы могли продолжить разработку, пока мы ждали официальных исправлений от поставщика.
Кстати, в наши дни это может фактически не работать - хотя я делал это раньше, есть вероятность, что такой небольшой цикл будет кэшироваться в кеше ЦП, и, поскольку переменная не помечена как изменчивая, кешированный код никогда не может подберите новое значение. Я никогда не видел этого в действии, но считаю, что теоретически это правда.
источник
b
это переменная метода. Вы действительно можете изменить переменную метода с помощью отражения? Независимо от общей точки зрения, мы не должны предполагать, что последняя строка недостижима.Это просто потому, что компилятор не слишком много работает с присмотром за детьми, хотя это возможно.
Показанный пример прост и разумен для компилятора, чтобы обнаружить бесконечный цикл. Но как насчет того, чтобы вставить 1000 строк кода без какой-либо связи с переменной
b
? А как насчет тех заявлений всеb = true;
? Компилятор определенно может оценить результат и сказать вам, что в конечном итоге он верен.while
цикле, но насколько медленным будет компиляция реального проекта?PS, инструмент lint определенно должен сделать это за вас.
источник
С точки зрения компилятора,
b
вwhile(b)
может где-то измениться на false. Компилятор просто не проверяет.Ради интереса попробуй
while(1 < 2)
иfor(int i = 0; i < 1; i--)
т. Д.источник
Выражения оцениваются во время выполнения, поэтому при замене скалярного значения «истина» чем-то вроде логической переменной вы изменили скалярное значение на логическое выражение, и, таким образом, компилятор не имеет возможности узнать его во время компиляции.
источник
Если компилятор может окончательно определить, что логическое значение будет оцениваться
true
во время выполнения, он выдаст эту ошибку. Компилятор предполагает, что объявленная вами переменная может быть изменена (хотя мы, как люди, знаем, что это не так).Чтобы подчеркнуть этот факт, если переменные объявлены, как
final
в Java, большинство компиляторов выдаст такую же ошибку, как если бы вы подставили значение. Это связано с тем, что переменная определяется во время компиляции (и не может быть изменена во время выполнения), и поэтому компилятор может окончательно определить, что выражение оцениваетсяtrue
во время выполнения.источник
Первый оператор всегда приводит к бесконечному циклу, потому что мы указали константу в условии цикла while, где, как и во втором случае, компилятор предполагает, что существует возможность изменения значения b внутри цикла.
источник