Ошибка недостижимого кода и предупреждение о мертвом коде в Java под Eclipse? - PullRequest
53 голосов
/ 26 января 2010

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

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) не имеет никакого эффекта,таким образом получая байт-код, который по существу идентичен?В какой момент применяется анализ достижимого кода?

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

Ответы [ 8 ]

34 голосов
/ 26 января 2010

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

Что касается того, почему 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 Elmination).

25 голосов
/ 26 января 2010

Недоступный код - ошибка согласно Спецификации языка Java .

Цитировать из 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; }

потому что утверждение х = 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 как услуга для программиста.

14 голосов
/ 26 января 2010

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

2 голосов
/ 26 января 2010

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

2 голосов
/ 26 января 2010

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

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

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

0 голосов
/ 29 марта 2016

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

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

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

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

0 голосов
/ 12 февраля 2014

Я попытался затмить и подумал, что есть три вида обработки мертвого кода в 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's control flow
    return null;
    System.out.println("am i dead");        
    }
0 голосов
/ 26 января 2010

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

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

...