Подтвердить что ты не робот

Ошибка недоступной ошибки кода или предупреждение о мертвом коде в 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!");
}

Только запускает предупреждение "Мертвый код"?

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

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

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

4b9b3361

Ответ 1

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

Что касается того, почему 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 говорит:

public class Test extends java.lang.Object{
public Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."":()V
   4:   return

public void foo();
  Code:
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc             #3; //String foo
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/StrV
   8:   return

public void bar();
  Code:
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc             #5; //String bar
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   11:  ldc             #5; //String bar
   13:  invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   16:  return

}

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

Ответ 2

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

Цитировать из JLS:

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

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

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

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

То есть компилятор, совместимый с Java, не может скомпилировать ваш первый фрагмент кода. Чтобы дополнительно указать JLS:

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

while (false) { x=3; }

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

if (false) { x=3; }

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

Второе предупреждение, которое Eclipse дает, о мертвом коде, - это предупреждение, генерируемое компилятором, которое не является "недоступным", согласно JLS, но на практике это. Это дополнительная проверка lint, которую предоставляет 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 в этой точке кода. В таком коротком фрагменте кода очевидно, что два оператора if тестируют одно и то же, однако, если в середине есть 10-15 строк кода, это может быть не так очевидно.

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

Ответ 3

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

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

Для типичного условного кода компиляции IF, JDK обнаруживает это и не сообщает об этом как мертвый код. Для мертвого кода, вызванного постоянным булевым флагом, JDK обнаруживает это и сообщает об этом на уровне предупреждения. Для мертвого кода, вызываемого потоком управления программой, 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 control flow
    return null;
    System.out.println("am i dead");        
    }

Ответ 8

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

  • Щелкните Window-Preferences-Java-Compiler-Errors/Warnings
  • Нажмите "Проблемы с потенциальным программированием"
  • Выберите "Игнорировать" "Мертвый код, например, если (false)"
  • Нажмите "Применить"
  • Нажмите "ОК"

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

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