Ошибка недоступной ошибки кода или предупреждение о мертвом коде в Java под Eclipse?

Кто-нибудь знает, почему:

public void foo() { System.out.println("Hello"); return; System.out.println("World!"); } 

Будет сообщено как «недостижимая ошибка» в Eclipse, но

 public void foo() { System.out.println("Hello"); if(true) return; System.out.println("World!"); } 

Только запускает предупреждение «Dead code»?

Единственное объяснение, о котором я могу думать, это то, что компилятор Java только флага первый, и что некоторый дополнительный анализ в Eclipse показывает второй. Однако, если это так, почему компилятор Java не может определить этот случай во время компиляции?

Разве компилятор Java не выяснил во время компиляции, что if (true) не имеет эффекта, что дает байт-код, который по существу идентичен? В какой момент применяется аналитический код достижимого кода?

Я предполагаю, что более общий способ думать об этом вопросе: «когда применяется применимый кодовый анализ»? При преобразовании второго fragmentа кода Java в конечный байт-код я уверен, что в какой-то момент удаляется эквивалент «if (true)», а представления двух программ становятся идентичными. Разве компилятор Java не повторил бы анализ своего достижимого кода?

    Первый не компилируется (вы получили сообщение об ошибке), второй компилятор (вы только что получили предупреждение). В этом разница.

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

    Обновление : JDK фактически устраняет мертвый код.

     public class Test { public void foo() { System.out.println("foo"); if(true)return; System.out.println("foo"); } public void bar() { System.out.println("bar"); if(false)return; System.out.println("bar"); } } 

    javap -c говорит:

     Открытый class Test extends java.lang.Object {
     public Test ();
       Код:
        0: aload_0
        1: вызывает специальный номер 1;  // Метод java / lang / Object. "" :() V
        4: возrotation
    
     public void foo ();
       Код:
        0: getstatic # 2;  // Поле java / lang / System.out: Ljava / io / PrintStream;
        3: ldc # 3;  // String foo
        5: invokevirtual # 4;  // Метод java / io / PrintStream.println: (Ljava / lang / StrV
        8: возrotation
    
     public void bar ();
       Код:
        0: getstatic # 2;  // Поле java / lang / System.out: Ljava / io / PrintStream;
        3: ldc # 5;  // Строка
        5: invokevirtual # 4;  // Метод java / io / PrintStream.println: (Ljava / lang / String;) V
        8: getstatic # 2;  // Поле java / lang / System.out: Ljava / io / PrintStream;
        11: ldc # 5;  // Строка
        13: invokevirtual # 4;  // Метод java / io / PrintStream.println: (Ljava / lang / String;) V
        16: возrotation
    
     }
    

    Что касается того, почему (Sun) не дает предупреждения об этом, я понятия не имею 🙂 По крайней мере, у компилятора JDK есть DCE (Dead Code Elimination).

    Недостижимый код является ошибкой в ​​соответствии с Java Language Spec .

    Процитировать из JLS:

    Идея состоит в том, что должен быть некоторый возможный путь выполнения от начала конструктора, метода, инициализатора экземпляра или статического инициализатора, который содержит инструкцию к самому утверждению. Анализ учитывает структуру утверждений. За исключением специального обращения while, do и для операторов, выражение условия которых имеет постоянное значение true, значения выражений не учитываются при анализе streamа.

    Это означает, что блок if не учитывается, так как если вы пройдете один из путей инструкции if , вы можете получить окончательный отчет о печати. Если вы изменили свой код:

     public void foo() { System.out.println("Hello"); if (true) return; else return; System.out.println("World!"); } 

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

    То есть компилятор, совместимый с Java, не может скомпилировать ваш первый fragment кода. Для дальнейшего цитирования JLS:

    В качестве примера следующий оператор приводит к ошибке времени компиляции:

     while (false) { x=3; } 

    потому что утверждение x = 3; недоступен; но внешне похожий случай:

     if (false) { x=3; } 

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

    Второе предупреждение, которое Eclipse дает, о мертвом коде, – это предупреждение, генерируемое компилятором, которое не является «недоступным», согласно JLS, но на практике это. Это дополнительная проверка стиля линта, которую предоставляет Eclipse. Это совершенно необязательно, и, используя конфигурацию Eclipse, можно отключить или превратить в ошибку компилятора вместо предупреждения.

    Этот второй блок является «запахом кода», if (false) блоки обычно вводятся, чтобы отключить код для целей отладки, оставив его, как правило, случайным и, следовательно, предупреждением.

    Фактически, Eclipse делает еще более сложные тесты, чтобы определить возможные значения для оператора if, чтобы определить, можно ли брать оба пути. Например, Eclipse также будет жаловаться на мертвый код следующим образом:

     public void foo() { System.out.println("Hello"); boolean bool = Random.nextBoolean(); if (bool) return; if (bool || Random.nextBoolean()) System.out.println("World!"); } 

    Он будет генерировать недостижимый код для второго оператора if, поскольку он может обосновать, что bool должен быть только false в этой точке кода. В таком коротком fragmentе кода очевидно, что два оператора if тестируют одно и то же, однако, если в середине есть 10-15 строк кода, это может быть не так очевидно.

    Итак, вкратце, разница между двумя: одна запрещена JLS, а другая – нет, но Eclipse обнаруживается как услуга для программиста.

    Это должно быть своего рода условная компиляция .
    Это не ошибка с if , но компилятор будет отмечать ошибку для while , do-while и for .
    Хорошо:

     if (true) return; // or false System.out.println("doing something"); 

    Это ошибки

     while (true) { } System.out.println("unreachable"); while (false) { System.out.println("unreachable"); } do { } while (true); System.out.println("unreachable"); for(;;) { } System.out.println("unreachable"); 

    Это объясняется в конце JLS 14.21: Недостижимые утверждения :

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

      static final boolean DEBUG = false; 

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

      if (DEBUG) { x=3; } 

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

    Значение if (true) немного более тонкое, чем «недостижимое»; потому что этот жестко закодированный return всегда сделает следующий код недостижимым, но изменение условия в if может сделать следующий оператор доступным.

    Наличие условного означает, что существует случайное условие, которое может измениться. Бывают случаи, когда в круглых скобках есть что-то более сложное, чем true , и человеческому читателю не очевидно, что следующий код «загроможден», но компилятор замечает, поэтому он может предупредить вас об этом.

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

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

    Разрешить if (true) return; это хороший способ обойти ограничение JLS, если вы действительно хотите сделать это специально. Если JLS остановит это, это будет мешать. Кроме того, он также должен остановиться:

      public static boolean DEBUG = true; //In some global class somewhere else ... if (DEBUG) return; //in a completely unrelated class. ... 

    Поскольку константа DEBUG полностью выровнена и функционально эквивалентна простому набору истины в условии if. С точки зрения JLS эти два случая очень похожи.

    Разница заключается в семантике между временем выполнения и временем компиляции. Во втором примере код компилируется в ветвь if-else в байт-коде, а eclipse просто достаточно умна, чтобы сказать вам, что часть else никогда не будет достигнута во время выполнения. Eclipse предупреждает вас только потому, что это все еще юридический код.

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

    Я сделал несколько попыток затмения и думаю, что есть 3 вида обработки мертвого кода JDK: 1) нет предупреждения, 2) предупреждения и 3) ошибки.

    Для типичного условного кода компиляции IF, JDK обнаруживает это и не сообщает об этом как мертвый код. Для мертвого кода, вызванного постоянным булевым флагом, JDK обнаруживает это и сообщает об этом на уровне предупреждения. Для мертвого кода, вызываемого streamом управления программой, JDK обнаруживает его как ошибку.

    Ниже моя попытка:

      public class Setting { public static final boolean FianlDebugFlag = false; } class B { ..... // no warn, it is typical "IF" conditional compilataion code if(Setting.FianlDebugFlag) System.out.println("am i dead?"); if(false) System.out.println("am i dead?"); // warn, as the dead code is caused by a constant boolean flag if(ret!=null && Setting.FianlDebugFlag) System.out.println("am i dead?"); if(Setting.FinalDebug) return null; System.out.println("am i dea?"); // error, as the dead code is due to the program's control flow return null; System.out.println("am i dead"); } 

    Если вы хотите игнорировать предупреждение «предупреждение о мертвом коде в Java под Eclipse», выполните следующие действия внутри eclipse *:

    1. Щелкните Window-Preferences-Java-Compiler-Errors / Warnings
    2. Нажмите «Потенциальные проблемы программирования»
    3. Выберите «Игнорировать» «Мертвый код, например, если (false)»
    4. Нажмите Применить
    5. Нажмите ОК.

    Сохраните и закройте среду разработки eclipse. При повторном открытии затмения эти конкретные предупреждения больше не должны отображаться.

    * Для этого примера я использую Eclipse IDE для разработчиков Java – Версия: Mars.2 Release (4.5.2)

    Давайте будем гением компьютера.