Почему у Java есть ошибка компилятора «недостижимое утверждение»? - PullRequest
79 голосов
/ 26 сентября 2010

Я часто нахожу, что при отладке программы удобно (хотя, возможно, и плохая практика) вставить оператор возврата в блок кода. Я мог бы попробовать что-то подобное в 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 пытается быть няней или есть веская причина сделать это ошибкой компилятора?

Ответы [ 8 ]

61 голосов
/ 26 сентября 2010

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

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

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


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

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

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


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

46 голосов
/ 26 сентября 2010

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

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, а затем правильно скомпилировать код без каких-либо других изменений в программе.текст.

18 голосов
/ 26 сентября 2010

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

14 голосов
/ 02 августа 2013

Я только что заметил этот вопрос и хотел добавить к нему свои $ .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' сохранили только в (который является просто локальной переменной # 0) что-то, совместимое с Object(так как код из строки 3 и далее будет обрабатывать локальную переменную # 0).

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

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

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

5 голосов
/ 26 сентября 2010

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

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

5 голосов
/ 26 сентября 2010

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

public void myMethod(){

    someCodeHere();

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

    moreCodeHere();

}

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

0 голосов
/ 09 июня 2012

Если причина для 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 / C ++, для большего контроля.

0 голосов
/ 26 сентября 2010

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

...