为什么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");
}
}
当然,这会产生编译器错误。
Test.java:7:无法访问的语句
我可以理解为什么警告可能是合理的,因为拥有未使用的代码是不好的做法。但我不明白为什么这需要生成错误。
这只是 Java 试图成为一名保姆,还是有充分的理由使其成为编译器错误?
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 ....
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
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
因为无法访问的代码对于编译器来说是没有意义的。虽然使代码对人们有意义比使其对编译器有意义既重要又困难,但编译器是代码的重要消费者。 Java 的设计者认为,对编译器没有意义的代码就是错误。他们的立场是,如果你有一些无法访问的代码,那么你就犯了一个需要修复的错误。
这里有一个类似的问题:Unreachable code: error or warning?,其中作者说:“我个人强烈认为这应该是一个错误:如果程序员编写了一段代码,那么它应该始终是为了在某些场景中实际运行它。”显然 Java 语言设计者也同意这一点。
无法访问的代码是否应该阻止编译是一个永远不会达成共识的问题。但这就是Java设计者这样做的原因。
许多人在评论中指出,有许多类无法访问的代码 Java 不会阻止编译。如果我正确理解哥德尔的后果,那么任何编译器都不可能捕获所有无法访问的代码类。
单元测试无法捕获每一个错误。我们不会以此作为反对其价值的论据。同样,编译器无法捕获所有有问题的代码,但它对于防止编译不良代码仍然很有价值。
Java 语言设计者将无法访问的代码视为错误。因此,尽可能阻止它编译是合理的。
(在你投反对票之前:问题不在于 Java 是否应该有一个无法访问的语句编译器错误。问题是为什么 Java 有一个无法访问的语句编译器错误。不要仅仅因为你认为 Java 就对我投反对票做出了错误的设计决策。)
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.
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.
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.
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.
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.
(Before you downvote: the question is not whether or not Java should have an unreachable statement compiler error. The question is why Java has an unreachable statement compiler error. Don't downvote me just because you think Java made the wrong design decision.)
没有明确的理由说明为什么不允许无法访问的语句;其他语言允许它们没有问题。对于您的特定需求,这是常见的技巧:
它看起来毫无意义,任何阅读代码的人都会猜测它一定是故意完成的,而不是使其余语句无法访问的粗心错误。
Java 对“条件编译”有一点支持
http: //java.sun.com/docs/books/jls/third_edition/html/statements.html#14.21
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:
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"
http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.21
是保姆。
我觉得 .Net 在这一点上是正确的 - 它会针对无法访问的代码发出警告,但不会引发错误。收到警告是件好事,但我认为没有理由阻止编译(特别是在调试会话期间,最好抛出一个 return 来绕过某些代码)。
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).
我刚刚注意到这个问题,并想添加我的 0.02 美元。
对于 Java,这实际上不是一个选项。 “无法访问的代码”错误并不是来自 JVM 开发人员认为要保护开发人员免受任何伤害或格外警惕,而是来自 JVM 规范的要求。
Java 编译器和 JVM 都使用所谓的“堆栈映射”——有关堆栈上所有项目的明确信息,为当前方法分配。堆栈中每个槽的类型都必须已知,这样 JVM 指令才不会将一种类型的项误认为是另一种类型。这对于防止将数值用作指针非常重要。使用 Java 程序集,可以尝试推送/存储数字,然后弹出/加载对象引用。然而,JVM 将在类验证期间拒绝此代码,即创建堆栈映射并测试一致性时。
为了验证堆栈映射,虚拟机必须遍历方法中存在的所有代码路径,并确保无论执行哪条代码路径,每条指令的堆栈数据都与任何先前代码推送的内容一致/存储在堆栈中。因此,在简单的情况下:
在第 3 行,JVM 将检查“if”的两个分支是否仅存储到与 Object 兼容的内容(只是本地 var#0)中(因为这就是第 3 行和on 将处理本地 var#0)。
当编译器到达无法访问的代码时,它不太知道堆栈此时可能处于什么状态,因此无法验证其状态。此时它无法再完全编译代码,因为它也无法跟踪局部变量,因此它不会将这种歧义留在类文件中,而是会产生致命错误。
当然,像
if (1<2)
这样的简单条件会愚弄它,但它并不是真正的愚弄 - 它给了它一个可能导致代码的潜在分支,并且至少编译器和VM 可以确定从那时起如何使用堆栈项。PS我不知道.NET在这种情况下会做什么,但我相信它也会编译失败。对于任何机器代码编译器(C、C++、Obj-C 等)来说,这通常不会成为问题
I only just noticed this question, and wanted to add my $.02 to this.
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.
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.
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:
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).
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.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.)
编译器的目标之一是排除错误类别。一些无法访问的代码是偶然出现的,很高兴 javac 在编译时排除了此类错误。
对于每条捕获错误代码的规则,有人会希望编译器接受它,因为他们知道自己在做什么。这是编译器检查的惩罚,而获得正确的平衡是语言设计的技巧之一。即使经过最严格的检查,仍然可以编写无数的程序,所以事情不会那么糟糕。
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.
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.
虽然我认为这个编译器错误是一件好事,但有一种方法可以解决它。
使用一个您知道为真的条件:
编译器不够聪明,无法抱怨这一点。
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:
The compiler is not smart enough to complain about that.
抱怨编译器越严格越好当然是一件好事,只要它允许你做你需要的事情。
通常,付出的小代价是注释掉代码,好处是当你编译代码时,它可以工作。一个常见的例子是 Haskell,人们对此尖叫,直到他们意识到他们的测试/调试只是主要测试,而且是简短的测试。我个人在 Java 中几乎不进行任何调试,但(实际上是故意的)不专心。
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.
if 允许的原因
if (aBooleanVariable) return; someMoreCode;
是允许flags,那么事实上if(true)return; someMoreCode;
不生成编译时错误似乎与生成 CodeNotReachable 异常的策略不一致,因为编译器“知道”true
不是标志(不是变量)。其他两种方法可能很有趣,但不适用于关闭部分方法代码以及
if (true) return
:现在,不要说
if (true) return ;
你可能想说assert false
并将-ea OR -ea package OR -ea className
添加到 jvm 参数中。好处是,这允许一定的粒度,并且需要向 jvm 调用添加额外的参数,因此不需要在代码中设置 DEBUG 标志,而是通过在运行时添加参数,这在目标不是目标时很有用开发机和重新编译&传输字节码需要时间。还有
System.exit(0)
方式,但这可能有点大材小用,如果你把它放在 JSP 中的 Java 中,那么它将终止服务器。除了 Java 被设计为“保姆”语言之外,我宁愿使用 C/C++ 之类的原生语言来获得更多控制。
If the reason for allowing
if (aBooleanVariable) return; someMoreCode;
is to allow flags, then the fact thatif (true) return; someMoreCode;
does not generate a compile time error seems like inconsistency in the policy of generating CodeNotReachable exception, since the compiler 'knows' thattrue
is not a flag (not a variable).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
:Now, instead of saying
if (true) return;
you might want to sayassert false
and add-ea OR -ea package OR -ea className
to 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.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.Apart from that Java is by-design a 'nanny' language, I would rather use something native like C/C++ for more control.