Eclipse 下 Java 中无法访问的代码错误与死代码警告?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2141029/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 04:03:33  来源:igfitidea点击:

Unreachable code error vs. dead code warning in Java under Eclipse?

javaeclipsedead-codeunreachable-code

提问by Uri

Does anyone know why:

有谁知道为什么:

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

Would be reported as an "unreachable error" under Eclipse, but

在 Eclipse 下会被报告为“无法访问的错误”,但是

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

Only triggers a "Dead code" warning?

只触发“死代码”警告?

The only explanation I can think of is that the Java compiler only flags the first, and that some extra analysis in Eclipse figures out the second. However, if that is the case, why can't the Java compiler figure out this case at compile time?

我能想到的唯一解释是 Java 编译器只标记第一个,而 Eclipse 中的一些额外分析会找出第二个。但是,如果是这样,为什么 Java 编译器在编译时就不能弄清楚这种情况呢?

Wouldn't the Java compiler figure out at compile time that the if(true) has no effect, thus yielding bytecode that is essentially identical? At what point is the reachable code analysis applied?

Java 编译器不会在编译时发现 if(true) 没有效果,从而产生本质上相同的字节码吗?什么时候应用可达代码分析?

I guess a more general way to think of this question is: "when is the reachable code analysis applied"? In the transformation of the second Java code fragment to the final bytecode, I am sure that at some point the "if(true)" runtime equivalent is removed, and the representations of the two programs become identical. Wouldn't the Java compiler then apply its reachable code analysis again?

我猜想这个问题的更一般方法是:“何时应用可达代码分析”?在将第二个 Java 代码片段转换为最终字节码的过程中,我确信在某个时候“if(true)”运行时等效项被删除,并且两个程序的表示变得相同。Java 编译器不会再次应用其可达代码分析吗?

采纳答案by BalusC

The first does notcompile (you got an error), the second compiles (you just got a warning). That's the difference.

第一个没有编译(你得到一个错误),第二个编译(你刚刚得到一个警告)。这就是区别。

As to why Eclipse detects dead code, well, that's just the convenience of an integrated development tool with a built-in compiler which can be finetuned more as opposed to JDK to detect this kind of code.

至于为什么Eclipse会检测死代码,嗯,这只是一个带有内置编译器的集成开发工具的便利,与JDK相比,它可以进行更多的微调来检测这种代码。

Update: the JDK actually eliminates dead code.

更新: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 -csays:

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

}

As to why it (Sun) doesn't give a warning about that, I have no idea :) At least the JDK compiler has actually DCE (Dead Code Elimination) builtin.

至于为什么它 (Sun) 没有对此发出警告,我不知道:) 至少 JDK 编译器实际上内置了 DCE(死代码消除)。

回答by Carl Smotricz

The if (true)is a little more subtle than "unreachable"; because that hard-coded returnwill always make the following code unreachable, but changing the condition in the ifcould make the following statement reachable.

if (true)有点不是“不可达”更微妙的; 因为硬编码return将始终使以下代码无法访问,但更改 中的条件if可能会使以下语句可以访问。

Having a conditional there means that there's a chance condition could change. There are cases where something more complicated than a trueis in the parentheses, and it's not obvious to the human reader that the following code is "deadened," but the compiler notices, so it's able to warn you about it.

在那里有条件意味着条件可能会改变。在某些情况下,true括号中的内容比 a 更复杂,对于人类读者来说,以下代码已“死机”并不明显,但编译器会注意到,因此它能够警告您。

Eclipse is mentioned here, and it makes things seem a little more complicated to the user; but actually underneath Eclipse is just a (very sophisticated) Java compiler that happens to feature a lot of switches for warnings etc. that Eclipse can switch on and off. In other words, you don't get quite the breadth of different warnings/errors from a straight javaccompile, nor do you have convenient means to turn all of them on or off. But it's the same deal, just with more bells and whistles.

这里提到了 Eclipse,它让用户觉得事情有点复杂;但实际上在 Eclipse 之下只是一个(非常复杂的)Java 编译器,它恰好具有许多用于警告等的开关,Eclipse 可以打开和关闭这些开关。换句话说,您无法从直接javac编译中获得大量不同的警告/错误,也没有方便的方法来打开或关闭所有警告/错误。但这是同样的交易,只是有更多的花里胡哨。

回答by Yoni

The difference is in the semantics between run-time and compile-time. In your second example, the code compiles to an if-else branch in the bytecode, and eclipse is simply smart enough to tell you that the else portion will never be reached in runtime. Eclipse only warns you, because it is still legal code.

区别在于运行时和编译时之间的语义。在您的第二个示例中,代码编译为字节码中的 if-else 分支,而 eclipse 非常聪明,可以告诉您在运行时永远不会到达 else 部分。Eclipse 只会警告您,因为它仍然是合法代码。

In your first example, it is an error because the code is illegal by the definition of java. The compiler doesn't allow you to create byte code with unreachable statements.

在您的第一个示例中,这是一个错误,因为该代码根据 java.lang 的定义是非法的。编译器不允许您使用无法访问的语句创建字节码。

回答by Paul Wagland

Unreachable code is an error according to the Java Language Spec.

根据Java 语言规范,无法访问的代码是一个错误。

To quote from the JLS:

引用 JLS:

The idea is that there must be some possible execution path from the beginning of the constructor, method, instance initializer or static initializer that contains the statement to the statement itself. The analysis takes into account the structure of statements. Except for the special treatment of while, do, and for statements whose condition expression has the constant value true, the values of expressions are not taken into account in the flow analysis.

这个想法是从包含语句的构造函数、方法、实例初始值设定项或静态初始值设定项的开头到语句本身必须有一些可能的执行路径。分析考虑了报表的结构。除了while、do和条件表达式为常量true的语句的特殊处理外,流分析中不考虑表达式的值。

What that means, is that the ifblock is not taken into account, since if you go through one of the paths of the ifstatement, you could reach final print statement. If you changed your code to be:

这意味着if不考虑该块,因为如果您通过if语句的其中一个路径,您可以到达最终的打印语句。如果您将代码更改为:

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

then suddenly it wouldn't compile anymore, since there is no path through the ifstatement that would allow the last line to be reached.

然后突然它不再编译了,因为没有通过if语句的路径可以到达最后一行。

That is, a Java compliant compiler is not allowed to compile your first code fragment. To further quote the JLS:

也就是说,Java 兼容编译器不允许编译您的第一个代码片段。进一步引用 JLS:

As an example, the following statement results in a compile-time error:

例如,以下语句会导致编译时错误:

while (false) { x=3; }

because the statement x=3; is not reachable; but the superficially similar case:

因为语句 x=3; 无法访问;但表面上相似的情况:

if (false) { x=3; }

does not result in a compile-time error. An optimizing compiler may realize that the statement x=3; will never be executed and may choose to omit the code for that statement from the generated class file, but the statement x=3; is not regarded as "unreachable" in the technical sense specified here.

不会导致编译时错误。优化编译器可能会意识到语句 x=3; 永远不会被执行,并且可以选择从生成的类文件中省略该语句的代码,但语句 x=3; 不被视为此处指定的技术意义上的“无法访问”。

The second warning that Eclipse gives, about dead code, is a warning generated by the compiler, that is not "unreachable", according to the JLS, but in practice is. This is an additional lintstyle check that Eclipse provides. This is entirely optional, and, by using the Eclipse configuration, can be disabled, or turned into a compiler error instead of a warning.

Eclipse 给出的关于死代码的第二个警告是编译器生成的警告,根据 JLS 的说法,这不是“无法访问”,但实际上是。这是Eclipse 提供的附加lint样式检查。这完全是可选的,并且通过使用 Eclipse 配置,可以被禁用,或者变成编译器错误而不是警告。

This second block is a "code smell", if (false)blocks are normally put in to disable code for debugging purposes, having it left behind is typically accidental, and hence the warning.

第二个块是“代码异味”,if (false)块通常用于禁用代码以用于调试目的,留下它通常是偶然的,因此会发出警告。

In fact, Eclipse does even more advanced tests to determine the possible values for an if statement to determine whether or not it is possible to take both paths. For example, Eclipse would also complain about dead code in the following method:

事实上,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!");
}

It will generate an unreachable code for the second if statement, since it can reason that boolmust only be falseat this point in the code. In such a short code fragment it is obvious that the two if statements are testing the same thing, however if there are 10-15 code lines in the middle it might not be so obvious anymore.

它将为第二个 if 语句生成一个无法访问的代码,因为它可以推断出它bool只能false在代码中的这一点。在这么短的代码片段中,很明显两个 if 语句正在测试相同的东西,但是如果中间有 10-15 行代码,它可能不再那么明显了。

So in summary, the difference between the two: one is forbidden by the JLS, and one is not, but is detected by Eclipse as a service to the programmer.

所以总结一下,两者的区别:一个是JLS禁止的,一个不是,但是被Eclipse检测为程序员的服务。

回答by Yishai

I think one way to some it up is that unreachable code is most likely a mistake, and the JLS tries to protect you from such mistakes.

我认为解决问题的一种方法是无法访问的代码很可能是一个错误,而 JLS 试图保护您免受此类错误的影响。

Allowing if (true) return;is a good way to work around the JLS limitation if you actually want to do this on purpose. If the JLS stopped this, it would be getting in the way. In addition, it should also stop:

if (true) return;如果您确实想故意这样做,则允许是解决 JLS 限制的好方法。如果 JLS 阻止了这一点,就会成为障碍。此外,它还应该停止:

 public static boolean DEBUG = true; //In some global class somewhere else


 ...

 if (DEBUG) return; //in a completely unrelated class.

 ...

Because the DEBUG constant is completely in-lined, and functionally equivalent to just typing a true in that if condition. From a JLS perspective those two cases are very similar.

因为 DEBUG 常量是完全内联的,并且在功能上等同于在 if 条件中输入 true。从 JLS 的角度来看,这两种情况非常相似。

回答by user85421

This is to allow a kind of conditionally compilation.
It is not an error with if, but the compiler will flag an error for while, do-whileand for.
This is OK:

这是为了允许一种有条件的编译
不是错误if,但编译器会标记错误whiledo-whilefor
还行吧:

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

This are errors

这是错误

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

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

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

for(;;) {
}
System.out.println("unreachable");


It is explained at the end of JLS 14.21: Unreachable Statements:

JLS 14.21: Unreachable Statements的末尾进行了解释:

The rationale for this differing treatment is to allow programmers to define "flag variables" such as:

 static final boolean DEBUG = false;

and then write code such as:

   if (DEBUG) { x=3; }

The idea is that it should be possible to change the value of DEBUG from false to true or from true to false and then compile the code correctly with no other changes to the program text.

这种不同处理的基本原理是允许程序员定义“标志变量”,例如:

 static final boolean DEBUG = false;

然后编写代码,例如:

   if (DEBUG) { x=3; }

这个想法是,应该可以将 DEBUG 的值从 false 更改为 true 或从 true 更改为 false,然后正确编译代码而不对程序文本进行其他更改。

回答by Houcheng

I made some try on eclipse and think there are 3 kinds of dead code handling of JDK: 1) no warn, 2) warn and 3) error.

我在 eclipse 上做了一些尝试,认为 JDK 有 3 种死代码处理:1)没有警告,2)警告和 3)错误。

For a typical "IF" conditional compilataion code, JDK detect this and didnt report it as dead code. For a dead code that is caused by a constant boolean flag, JDK detect this and report it at warning level. For dead code that is cause by the program's control flow, JDK detect it as error.

对于典型的“IF”条件编译代码,JDK 会检测到这一点,并且不会将其报告为死代码。对于由常量布尔标志引起的死代码,JDK 会检测到并在警告级别报告。对于由程序的控制流引起的死代码,JDK 将其检测为错误。

Below is my try:

下面是我的尝试:

    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");        
    }

回答by Mark Burleigh

If you wish to ignore the warning "dead code warning in Java under Eclipse" do the following inside eclipse*:

如果您希望忽略警告“Eclipse 下 Java 中的死代码警告”,请在 eclipse* 中执行以下操作:

  1. Click Window-Preferences-Java-Compiler-Errors/Warnings
  2. Click on "Potential Programming Problems"
  3. Choose "Ignore" the "Dead Code eg if(false)"
  4. Click Apply
  5. Click OK
  1. 单击窗口-首选项-Java-编译器-错误/警告
  2. 点击“潜在的编程问题”
  3. 选择“忽略”“死代码,例如 if(false)”
  4. 点击应用
  5. 单击确定

Save and close your eclipse IDE When you reopen eclipse, these specific warnings should no longer be listed.

保存并关闭您的 eclipse IDE 当您重新打开 eclipse 时,不应再列出这些特定警告。

*For this example solution I'm using Eclipse IDE for Java Developers - Version: Mars.2 Release (4.5.2)

*对于这个示例解决方案,我使用 Eclipse IDE for Java Developers - 版本:Mars.2 Release (4.5.2)