Почему требуется возврат даже после System.exit(0);

Рассмотрим эту функцию:

public boolean foo(){
   System.exit(1);
   //The lines beyond this will not be read
   int bar = 1;                  //L1
   //But the return statement is required for syntactically correct code
   return false;                 //L2

   //error here for unreachable code
   //int unreachable = 3;        //L3

}

Может кто-нибудь объяснить, почему L1 и L2 явно недоступны, не дают предупреждений, но L3 делает.

Ответ 1

Поскольку в отношении компилятора System.exit() - это еще один вызов метода.

Тот факт, что то, что он делает, - это конец, процесс может быть найден только из реализации (который является нативным кодом, а не тем, что он имеет какое-либо значение).

Если вам нужно положить System.exit() в свой код (обычно лучше всего его избежать, если вы не хотите возвращать код, отличный от 0), он должен действительно быть в методе, который возвращает void, main() например. Это лучше.

Что касается достижимости, то объяснение одно и то же: return - это ключевое слово языка Java, поэтому компилятор или анализатор, используемые IDE, могут сказать, что теоретически невозможно для кода после инструкции return казнены. Эти правила определены здесь.

Ответ 2

Компилятор Java ничего не знает о System.exit. Это всего лишь метод, насколько это касается, поэтому конец заявления доступен.

Вы говорите, что L1 и L2 "явно недоступны", но это только потому, что вы знаете, что делает System.exit. Язык не делает этого, тогда как он знает, что делает оператор return, поэтому он знает, что L3 действительно недоступен.

Я иногда думаю, что было бы полезно объявить, что метод не просто void, но никогда не заканчивается нормально - он никогда не возвращается (хотя он может вызвать исключение). Затем компилятор сможет использовать эту информацию, чтобы сделать конец любого вызывающего выражения недостижимым, не позволяя подобной вещи быть проблемой. Однако, что только мои мечты о дизайне языка - Java не имеет ничего подобного, и было бы очень плохой идеей для компилятора "знать", что некоторые JRE-методы никогда не вернутся нормально, когда эта концепция не может быть выражена непосредственно внутри языка.

Вместо этого компилятор связан правилами раздела 14.21 JLS, включая:

  • Первый оператор в непустом блоке, который не является блоком коммутатора, доступен, если блок доступен.
  • Каждый другой оператор S в непустом блоке, который не является блоком коммутатора, доступен, если утверждение, предшествующее S, может завершиться нормально.

...

Оператор выражения может нормально завершаться, если он доступен.

(вызов метода - выражение).

Затем из раздела 8.4.7:

Если у метода объявлен тип возврата, тогда возникает ошибка времени компиляции, если тело метода может нормально функционировать (§14.1).

и в 14.1:

Если не указано иное, оператор завершается нормально, если все выражения, которые он оценивает, и все подзадачи, которые он выполняет, выполняются нормально.

Таким образом, вызов System.exit() может нормально выполняться в отношении компилятора, что означает, что тело метода foo может завершиться нормально, что приводит к ошибке.

Ответ 3

С точки зрения языка существует только 2 способа избежать текущей области: return и throw. Вызов методов никогда не рассматривается одинаково, даже если они состоят из единственной строки кода:

void method() {
  throw new RuntimeException();
}

Еще больше. Теоретически любой вызов метода может вызвать выброс RuntimeException. В этом случае компилятор должен, вероятно, дать вам предупреждения для абсолютно любого вызова метода, который не находится внутри блока try:

...
method(); // WARNING: may throw in theory
anotherMethod(); // WARNING: possible unreachable code, also may throw
anotherMethod2(); // WARNING: possible unreachable code, also may throw
// etc
...

Для вашей логики вопроса то же самое.

Ответ 4

Me: "Может ли что-нибудь быть выполнено после оператора возврата?"
Java: "НЕТ".

Me: "Может ли что-нибудь быть выполнено после вызова System.exit?"
Java: "Это метод, я не знаю, что он делает - это не зарезервированное ключевое слово, оно не влияет на поток программы, насколько я знаю" (и это может даже не работать (я не знаю, может генерировать исключения (или будущие варианты)))

Ответ 5

Если объявлен метод для возврата значения не-void, он должен содержать оператор return где-нибудь, даже если он никогда не был достигнут (например, в коде в вопросе).

С точки зрения компилятора System.exit() - это еще один вызов метода, при этом ничего особенного в нем не указывает, что программа заканчивается, как только она достигает. Только вы, как программист, знаете этот факт, но это нечто вне знаний компилятора.

О второй части вашего вопроса - ничто не может пойти после инструкции return в блоке кода внутри метода, поскольку это всегда будет недостижимый код. Это объясняет, почему компилятор жалуется на линию L3.

Ответ 6

Компилятор проверяет, доступен ли какой-то код только по отношению к ключевому слову return (в общем случае, также throw and break (в случае циклов)). Для компилятора вызов метода exit - это еще один вызов, он не знает его значения, поэтому он не знает, что код впоследствии никогда не будет достигнут.

Ответ 7

Я знаю, что это не имеет смысла, и все же это то, как работает компилятор Java, когда вы вызываете метод.
Компилятор в данный момент не знает, что делает Sytem.exist(почему Rt.jar должен отличаться от других комбайнов, с которыми вы компилируете, в этом смысле?).
Это контрастно, например, для следующего фрагмента кода -

 public int test() {
  throw new NullPointerException("aaaa");
}

Если компилятор может сказать, что исключение всегда выбрасывается, и поэтому возврат не требуется.

Ответ 8

Возможно, инструмент анализа статического кода не учитывает, что приложение завершает работу.