为什么 Java 会出现“unreachable statement”编译器错误?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3795585/
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-14 05:04:36  来源:igfitidea点击:

Why does Java have an "unreachable statement" compiler error?

javalanguage-designunreachable-statement

提问by Mike

I often find when debugging a program it is convenient, (although arguably bad practice) to insert a return statement inside a block of code. I might try something like this in Java ....

我经常发现在调试程序时在代码块中插入 return 语句很方便(尽管可以说是不好的做法)。我可能会在 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");
        }
}

of course, this would yield the compiler error.

当然,这会产生编译器错误。

Test.java:7: unreachable statement

Test.java:7:无法访问的语句

I could understand why a warning might be justified as having unused code is bad practice. But I don't understand why this needs to generate an error.

我可以理解为什么警告可能会被证明具有未使用的代码是不好的做法。但我不明白为什么这需要产生错误。

Is this just Java trying to be a Nanny, or is there a good reason to make this a compiler error?

这只是Java试图成为保姆,还是有充分的理由使它成为编译器错误?

采纳答案by SamStephens

Because unreachable code is meaningless to the compiler. Whilst making code meaningful to people is both paramount and harder than making it meaningful to a compiler, the compiler is the essential consumer of code. The designers of Java take the viewpoint that code that is not meaningful to the compiler is an error. Their stance is that if you have some unreachable code, you have made a mistake that needs to be fixed.

因为无法访问的代码对编译器毫无意义。虽然让代码对人们有意义比让代码对编译器有意义更重要也更难,但编译器是代码的基本消费者。Java 的设计者认为对编译器没有意义的代码是错误的。他们的立场是,如果您有一些无法访问的代码,那么您就犯了一个需要修复的错误。

There is a similar question here: Unreachable code: error or warning?, in which the author says "Personally I strongly feel it should be an error: if the programmer writes a piece of code, it should always be with the intention of actually running it in some scenario." Obviously the language designers of Java agree.

这里有一个类似的问题:Unreachable code: error or warning? ,其中作者说“我个人强烈认为应该是一个错误:如果程序员编写一段代码,它应该始终带有在某些场景中实际运行它的意图。” 显然,Java 的语言设计者同意这一点。

Whether unreachable code should prevent compilation is a question on which there will never be consensus. But this is why the Java designers did it.

不可达代码是否应该阻止编译是一个永远不会达成共识的问题。但这就是 Java 设计者这样做的原因。



A number of people in comments point out that there are many classes of unreachable code Java doesn't prevent compiling. If I understand the consequences of G?del correctly, no compiler can possibly catch all classes of unreachable code.

许多人在评论中指出,Java 不会阻止编译的许多类无法访问的代码。如果我正确理解了 G?del 的后果,则没有编译器可以捕获所有无法访问的代码类。

Unit tests cannot catch every single bug. We don't use this as an argument against their value. Likewise a compiler can't catch all problematic code, but it is still valuable for it to prevent compilation of bad code when it can.

单元测试无法捕获每一个错误。我们不会将此用作反对其价值的论据。同样,编译器无法捕获所有有问题的代码,但在可能的情况下防止编译错误代码仍然很有价值。

The Java language designers consider unreachable code an error. So preventing it compiling when possible is reasonable.

Java 语言设计者将无法访问的代码视为错误。因此,在可能的情况下阻止它编译是合理的。



(Before you downvote: the question is not whether or not Java should have an unreachable statement compiler error. The question is whyJava has an unreachable statement compiler error. Don't downvote me just because you think Java made the wrong design decision.)

(在你投反对票之前:问题不在于 Java 是否应该有一个无法访问的语句编译器错误。问题是为什么Java 有一个无法访问的语句编译器错误。不要仅仅因为你认为 Java 做出了错误的设计决定就对我投反对票。)

回答by Brady Moritz

It is Nanny. I feel .Net got this one right - it raises a warning for unreachable code, but not an error. It is good to be warned about it, but I see no reason to prevent compilation (especially during debugging sessions where it is nice to throw a return in to bypass some code).

是保姆。我觉得 .Net 做对了这一点——它对无法访问的代码发出警告,但不是错误。得到警告是件好事,但我认为没有理由阻止编译(尤其是在调试会话期间,最好抛出返回以绕过某些代码)。

回答by Sean Patrick Floyd

While I think this compiler error is a good thing, there is a way you can work around it. Use a condition you know will be true:

虽然我认为这个编译器错误是一件好事,但有一种方法可以解决它。使用一个你知道会成立的条件:

public void myMethod(){

    someCodeHere();

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

    moreCodeHere();

}

The compiler is not smart enough to complain about that.

编译器不够聪明,无法抱怨这一点。

回答by Ricky Clarkson

One of the goals of compilers is to rule out classes of errors. Some unreachable code is there by accident, it's nice that javac rules out that class of error at compile time.

编译器的目标之一是排除错误类别。一些无法访问的代码是偶然出现的,javac 在编译时排除了该类错误,这很好。

For every rule that catches erroneous code, someone will want the compiler to accept it because they know what they're doing. That's the penalty of compiler checking, and getting the balance right is one of the tricker points of language design. Even with the strictest checking there's still an infinite number of programs that can be written, so things can't be that bad.

对于捕获错误代码的每条规则,有人会希望编译器接受它,因为他们知道自己在做什么。这是编译器检查的惩罚,而获得正确的平衡是语言设计的技巧之一。即使经过最严格的检查,仍然可以编写无限数量的程序,所以事情不会那么糟糕。

回答by Jér?me JEAN-CHARLES

It is certainly a good thing to complain the more stringent the compiler is the better, as far as it allows you to do what you need. Usually the small price to pay is to comment the code out, the gain is that when you compile your code works. A general example is Haskell about which people screams until they realize that their test/debugging is main test only and short one. I personally in Java do almost no debugging while being ( in fact on purpose) not attentive.

抱怨编译器越严格越好,这当然是一件好事,只要它允许您做您需要的事情。通常付出的小代价是将代码注释掉,收获是当你编译你的代码时。一个普遍的例子是 Haskell,人们会尖叫,直到他们意识到他们的测试/调试只是主要测试和简短测试。我个人在 Java 中几乎不做任何调试,同时(实际上是故意的)不专心。

回答by irreputable

There is no definitive reason why unreachable statements must be not be allowed; other languages allow them without problems. For your specific need, this is the usual trick:

没有明确的理由为什么必须不允许无法访问的语句;其他语言允许他们没有问题。对于您的特定需求,这是通常的技巧:

if (true) return;

It looks nonsensical, anyone who reads the code will guess that it must have been done deliberately, not a careless mistake of leaving the rest of statements unreachable.

看起来很荒谬,任何阅读代码的人都会猜测它一定是故意完成的,而不是让其余语句无法访问的粗心错误。

Java has a little bit support for "conditional compilation"

Java 对“条件编译”有一点支持

http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.21

http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.21

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.

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.

if (false) { x=3; }

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

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

static final boolean DEBUG = false;

然后编写代码,例如:

if (DEBUG) { x=3; }

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

回答by MichaelS

If the reason for allowing if (aBooleanVariable) return; someMoreCode;is to allow flags, then the fact that if (true) return; someMoreCode;does not generate a compile time error seems like inconsistency in the policy of generating CodeNotReachable exception, since the compiler 'knows' that trueis not a flag (not a variable).

如果允许的原因if (aBooleanVariable) return; someMoreCode;是允许标志,那么if (true) return; someMoreCode;不生成编译时错误的事实似乎与生成 CodeNotReachable 异常的策略不一致,因为编译器“知道”这true不是标志(不是变量)。

Two other ways which might be interesting, but don't apply to switching off part of a method's code as well as if (true) return:

另外两种可能有趣但不适用于关闭部分方法代码的方法以及if (true) return

Now, instead of saying if (true) return;you might want to say assert falseand add -ea OR -ea package OR -ea classNameto the jvm arguments. The good point is that this allows for some granularity and requires adding an extra parameter to the jvm invocation so there is no need of setting a DEBUG flag in the code, but by added argument at runtime, which is useful when the target is not the developer machine and recompiling & transferring bytecode takes time.

现在,不是说if (true) return;您可能想说assert false并添加-ea OR -ea package OR -ea className到 jvm 参数中。好处是这允许一些粒度,并且需要向 jvm 调用添加额外的参数,因此不需要在代码中设置 DEBUG 标志,而是在运行时添加参数,这在目标不是开发人员机器和重新编译和传输字节码需要时间。

There is also the System.exit(0)way, but this might be an overkill, if you put it in Java in a JSP then it will terminate the server.

还有一种System.exit(0)方法,但这可能是一种矫枉过正,如果您将它放在 JSP 中的 Java 中,那么它将终止服务器。

Apart from that Java is by-design a 'nanny' language, I would rather use something native like C/C++ for more control.

除了 Java 被设计为一种“保姆”语言之外,我宁愿使用像 C/C++ 这样的本机语言来进行更多控制。

回答by Pawel Veselov

I only just noticed this question, and wanted to add my $.02 to this.

我只是注意到这个问题,并想为此添加我的 0.02 美元。

In case of Java, this is not actually an option. The "unreachable code" error doesn't come from the fact that JVM developers thought to protect developers from anything, or be extra vigilant, but from the requirements of the JVM specification.

在 Java 的情况下,这实际上不是一个选项。“无法访问的代码”错误并非来自 JVM 开发人员认为要保护开发人员免受任何影响或格外警惕这一事实,而是来自 JVM 规范的要求。

Both Java compiler, and JVM, use what is called "stack maps" - a definite information about all of the items on the stack, as allocated for the current method. The type of each and every slot of the stack must be known, so that a JVM instruction doesn't mistreat item of one type for another type. This is mostly important for preventing having a numeric value ever being used as a pointer. It's possible, using Java assembly, to try to push/store a number, but then pop/load an object reference. However, JVM will reject this code during class validation,- that is when stack maps are being created and tested for consistency.

Java 编译器和 JVM 都使用所谓的“堆栈映射”——堆栈上所有项目的明确信息,如分配给当前方法。必须知道堆栈中每个插槽的类型,以便 JVM 指令不会将一种类型的项目误认为另一种类型。这对于防止将数值用作指针非常重要。可以使用 Java 程序集尝试推送/存储一个数字,然后弹出/加载一个对象引用。但是,JVM 将在类验证期间拒绝此代码,即在创建堆栈映射并测试一致性时。

To verify the stack maps, the VM has to walk through all the code paths that exist in a method, and make sure that no matter which code path will ever be executed, the stack data for every instruction agrees with what any previous code has pushed/stored in the stack. So, in simple case of:

为了验证堆栈映射,VM 必须遍历方法中存在的所有代码路径,并确保无论哪条代码路径将被执行,每条指令的堆栈数据都与之前任何代码推送的内容一致/ 存储在堆栈中。所以,在简单的情况下:

Object a;
if (something) { a = new Object(); } else { a = new String(); }
System.out.println(a);

at line 3, JVM will check that both branches of 'if' have only stored into a (which is just local var#0) something that is compatible with Object (since that's how code from line 3 and on will treat local var#0).

在第 3 行,JVM 将检查 'if' 的两个分支是否只存储到一个(只是本地 var#0)与 Object 兼容的东西中(因为这就是第 3 行及以后的代码将如何处理本地 var#0 )。

When compiler gets to an unreachable code, it doesn't quite know what state the stack might be at that point, so it can't verify its state. It can't quite compile the code anymore at that point, as it can't keep track of local variables either, so instead of leaving this ambiguity in the class file, it produces a fatal error.

当编译器遇到无法访问的代码时,它不太清楚堆栈此时可能处于什么状态,因此无法验证其状态。那时它不能完全编译代码,因为它也不能跟踪局部变量,因此它不会在类文件中留下这种歧义,而是会产生致命错误。

Of course a simple condition like if (1<2)will fool it, but it's not really fooling - it's giving it a potential branch that can lead to the code, and at least both the compiler and the VM can determine, how the stack items can be used from there on.

当然,像这样的简单条件if (1<2)会愚弄它,但它并不是真正的愚弄 - 它给了它一个可以通向代码的潜在分支,并且至少编译器和 VM 都可以确定如何从那里使用堆栈项在。

P.S. I don't know what .NET does in this case, but I believe it will fail compilation as well. This normally will not be a problem for any machine code compilers (C, C++, Obj-C, etc.)

PS 我不知道 .NET 在这种情况下做了什么,但我相信它也会编译失败。对于任何机器代码编译器(C、C++、Obj-C 等),这通常不会成为问题。