Почему у Java есть ошибка компилятора "недостижимый оператор"?

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

class Test {
        public static void main(String args[]) {
                System.out.println("hello world");
                return;
                System.out.println("i think this line might cause a problem");
        }
}

конечно, это приведет к ошибке компилятора.

Test.java:7: недостижимый оператор

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

Является ли это просто Java, пытающимся быть няней или есть веская причина сделать это ошибкой компилятора?

Ответ 1

Потому что недостижимый код не имеет смысла для компилятора. Хотя сделать код значимым для людей является как первостепенным, так и сложнее, чем сделать его значимым для компилятора, компилятор является основным потребителем кода. Разработчики Java считают, что код, который не имеет смысла для компилятора, является ошибкой. Их позиция в том, что если у вас есть недостижимый код, вы сделали ошибку, которая должна быть исправлена.

Здесь есть аналогичный вопрос: Недостижимый код: ошибка или предупреждение?, в котором автор говорит: "Лично я сильно чувствую, что это должна быть ошибка: если программист пишет фрагмент кода, он всегда должен быть с намерением фактически запустить его в каком-то сценарии". Очевидно, что разработчики языка Java согласны.

Должен ли недостижимый код предотвращать компиляцию, это вопрос, на котором никогда не будет консенсуса. Но именно поэтому разработчики Java сделали это.


Несколько человек в комментариях указывают, что существует много классов недостижимого кода Java, который не мешает компиляции. Если я правильно понимаю последствия Gödel, ни один компилятор не сможет поймать все классы недостижимого кода.

Ед. тесты не могут уловить каждую ошибку. Мы не используем это как аргумент против их ценности. Подобным образом компилятор не может уловить все проблемные коды, но он по-прежнему ценен для предотвращения компиляции плохого кода, когда это возможно.

Разработчики языка Java считают недостижимый код ошибкой. Поэтому предотвращение его компиляции, когда это возможно, разумно.


(Перед тем, как сделать downvote: вопрос заключается не в том, должна ли Java иметь недостижимую ошибку компилятора оператора. Вопрос в том, почему в Java есть недопустимая ошибка компилятора оператора. Не уменьшайте меня только потому, что вы думаете, что Java сделал неправильный дизайн решение.)

Ответ 2

Нет окончательной причины, по которой недопустимые утверждения не должны допускаться; другие языки позволяют им без проблем. Для вашей конкретной потребности это обычный трюк:

if (true) return;

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

Java немного поддерживает "условную компиляцию"

http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.21

if (false) { x=3; }

не приводит к времени компиляции ошибка. Оптимизирующий компилятор может что утверждение x = 3; будем никогда не выполняются и могут опустить код для этого утверждения из сгенерированный файл класса, но оператор x = 3; не рассматривается как "недостижимый" в техническом смысле указанных здесь.

Обоснование этого различия лечение должно позволить программистам определить "переменные флага", такие как:

static final boolean DEBUG = false;

а затем напишите код, например:

if (DEBUG) { x=3; }

Идея заключается в том, что она должна быть возможной изменить значение DEBUG из false - true или true от false и затем скомпилировать код правильно без каких-либо изменений в программе текст.

Ответ 3

Это Няня. Я чувствую, что .Net получил это право - он вызывает предупреждение о недостижимом коде, но не ошибку. Приятно об этом предупреждать, но я не вижу причин для предотвращения компиляции (особенно во время сеансов отладки, когда было бы неплохо бросить возврат в обход некоторого кода).

Ответ 4

Я только что заметил этот вопрос и хотел добавить к нему $.02.

В случае Java это на самом деле не вариант. Ошибка "недостижимого кода" исходит не из того, что разработчики JVM решили защитить разработчиков от чего-либо или быть более бдительными, но из требований спецификации JVM.

Как Java-компилятор, так и JVM используют так называемые "карты стека" - определенную информацию обо всех элементах стека, выделенных для текущего метода. Тип каждого слота стека должен быть известен, так что команда JVM не будет беспокоить элемент одного типа для другого типа. Это в основном важно для предотвращения использования числового значения в качестве указателя. Возможно, используя сборку Java, попытаться нажать/сохранить номер, но затем поп/загрузить ссылку на объект. Однако JVM будет отклонять этот код во время проверки класса, то есть когда создаются карты стека и проверяются на согласованность.

Чтобы проверить карты стека, виртуальная машина должна пройти через все пути кода, существующие в методе, и убедиться, что независимо от того, какой путь кода будет выполняться, данные стека для каждой команды согласуются с тем, что было ранее код вытолкнул/сохранил в стеке. Итак, в простом случае:

Object a;
if (something) { a = new Object(); } else { a = new String(); }
System.out.println(a);

в строке 3, JVM проверит, что обе ветки "if" сохраняются только в (который является только локальным var # 0) чем-то, что совместимо с Object (так как код из строки 3 и далее будет обрабатывать локальные вар # 0).

Когда компилятор добирается до недостижимого кода, он не совсем знает, в каком состоянии может находиться стек, и не может проверить его состояние. В этом случае он больше не может компилировать код, так как он также не может отслеживать локальные переменные, поэтому вместо того, чтобы оставлять эту двусмысленность в файле класса, возникает фатальная ошибка.

Конечно, простое условие, подобное if (1<2), обманет его, но на самом деле это не обманывает - это дает ему потенциальную ветвь, которая может привести к коду, и по крайней мере как компилятор, так и виртуальная машина могут определить, как стек пункты могут быть использованы оттуда.

P.S. Я не знаю, что делает .NET в этом случае, но я считаю, что это тоже не скомпилит. Это обычно не будет проблемой для компиляторов машинного кода (C, С++, Obj-C и т.д.).

Ответ 5

Хотя я думаю, что эта ошибка компилятора - это хорошо, есть способ, которым вы можете обойти это. Используйте условие, которое, как вы знаете, будет правдой:

public void myMethod(){

    someCodeHere();

    if(1 < 2) return; // compiler isn't smart enough to complain about this

    moreCodeHere();

}

Компилятор недостаточно умен, чтобы жаловаться на это.

Ответ 6

Одной из целей компиляторов является исключение классов ошибок. Некоторый недостижимый код существует случайно, хорошо, что javac исключает этот класс ошибок во время компиляции.

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

Ответ 7

Конечно, неплохо жаловаться на более строгий компилятор, тем лучше, насколько он позволяет вам делать то, что вам нужно. Обычно небольшая цена, которую нужно заплатить, заключается в том, чтобы прокомментировать код, выигрыш в том, что при компиляции кода работает. Общий пример - Haskell, о котором люди кричат, пока не осознают, что их тестирование/отладка является основным тестовым и коротким. Я лично на Java практически не отлаживаю себя, будучи (по сути, специально) не внимательным.

Ответ 8

Если причиной допускать if (aBooleanVariable) return; someMoreCode; является разрешение флажков, то тот факт, что if (true) return; someMoreCode; не генерирует ошибку времени компиляции, кажется несогласованностью в политике генерации исключения CodeNotReachable, поскольку компилятор "знает", что true не является флагом (а не переменной).

Два других способа, которые могут быть интересны, но не применяются для отключения части кода метода, а также if (true) return:

Теперь вместо того, чтобы говорить if (true) return;, вы можете сказать assert false и добавить -ea OR -ea package OR -ea className в аргументы jvm. Хорошим моментом является то, что это допускает некоторую детализацию и требует добавления дополнительного параметра к вызову jvm, поэтому нет необходимости устанавливать флаг DEBUG в коде, но добавленный аргумент во время выполнения, что полезно, когда цель не является машина разработчика и перекомпиляция и передача байт-кода требуют времени.

Существует также способ System.exit(0), но это может быть перебор, если вы поместите его на Java в JSP, то он завершит работу с сервером.

Помимо того, что Java по-умолчанию является языком "няня", я бы скорее использовал что-то вроде C/С++ для большего контроля.