为啥 Java 编译器不会为无法访问的 then 语句生成无法访问的语句错误?

Posted

技术标签:

【中文标题】为啥 Java 编译器不会为无法访问的 then 语句生成无法访问的语句错误?【英文标题】:Why does a Java Compiler not produce an unreachable statement error for an unreachable then statement?为什么 Java 编译器不会为无法访问的 then 语句生成无法访问的语句错误? 【发布时间】:2016-03-30 22:41:31 【问题描述】:

如果我尝试编译

for(;;)



System.out.println("End");

Java 编译器生成错误消息Unreachable statement。但是如果我添加另一个“unreachable”(根据我)break 声明并使它:

for(;;)

    if(false) break;

System.out.println("End");

它编译。为什么它不会产生错误?

【问题讨论】:

我不明白您的编辑:无论 if... 中使用的条件如何,它都是一个无限循环... @assylias 我不认为在这里简单地解决 if 是一个足够的答案,否则它会是重复的。诀窍是 if 后门使编译器认为中断可达,它展开以提供退出条件。 @chrylis 是的,我明白 - 我在评论中的意思是 continue 不会退出任何东西...... @JarrodRoberson 我特别不认为这是重复的,因为这个问题涉及交互,虽然取决于特殊情况,但需要进一步分析。 @assylias 我补充说,因为 JLS 描述说 A break, continue, return, or throw statement cannot complete normally.。即使使用 return 也会产生错误。 【参考方案1】:

行为定义在the JLS description of unreachable statements:

当 if-then 语句可达时,then-语句可达。

因此,无论if 中的条件如何,编译器都会确定 then 语句 (break;) 是可访问的。

还有一点,强调我的:

一个基本的for 语句可以正常完成,只要满足以下至少一项为真:

for 语句可达,存在条件表达式,且条件表达式不是值为 true 的常量表达式(第 15.28 节)。 有一个可访问的break 语句退出 for 语句。

所以 for 可以正常完成,因为 then 语句包含 break。正如您所注意到的,如果您将 break 替换为 return,它将不起作用。


基本原理在本节末尾进行了解释。实质上,if 有一个特殊的处理方式,允许以下构造:

if(DEBUG)  ... 

其中 DEBUG 可能是编译时间常数。

【讨论】:

在该描述中,有一行表示如果:A break, continue, return, or throw statement cannot complete normally. ,将产生错误。好吧,continuereturn 就可以了。但不是break。 :// @PriydarshiSingh 不相关。 对“正常完成”部分进行了很好的分析。 @black "iff" 是数学中“当且仅当”的常用缩写。 @black 为什么不呢?这是an English word...【参考方案2】:

如my answer to a similar question 中所述,特定构造if(compile-time-false) 作为显式后门不受不可达性规则的约束。在这种情况下,编译器会将您的break 视为可访问的。

【讨论】:

【参考方案3】:

来自the JLS

if-then 语句可以正常完成,如果至少有一个 以下是正确的:

> if-then 语句可达,条件表达式不可达 一个值为真的常量表达式。

> then-语句可以正常完成。

所以if(false) 是允许的。

这种“有条件编译”的能力对, 以及与二进制兼容性的关系。如果一组类 使用这样的“标志”变量被编译并且条件代码是 省略,稍后仅分发新版本是不够的 包含标志定义的类或接口。一种 因此,对标志值的更改不是二进制兼容的 与预先存在的二进制文件。 (还有其他原因 这种不兼容也是如此,例如使用常量以防万一 switch 语句中的标签;)

【讨论】:

【参考方案4】:

基本上,无法访问的代码是通过静态地在没有实际运行代码的情况下分析程序来检测的。而条件将在 runtime 进行检查。因此,当进行此分析时,它实际上并没有查看条件,而只是检查break; 是否可以通过if 访问(可访问)。

【讨论】:

【参考方案5】:

Java 没有检测到所有不可访问语句的核心原因是通常无法回答代码是否可访问。这是因为halting problem 在图灵机上是不可判定的。

那么,很明显,所有无法到达的语句都无法被检测到,但为什么不尝试评估条件呢?现在想象一下,使用的条件不仅仅是false,而是~x == x。例如,所有这些语句将为每个int x (source) 打印true

    System.out.println((x + x & 1) == 0);
    System.out.println((x + -x & 1) == 0);
    System.out.println((-x & 1) == (x & 1));
    System.out.println(((-x ^ x) & 1) == 0);
    System.out.println((x * 0x80 & 0x56) == 0);
    System.out.println((x << 1 ^ 0x1765) != 0);

语句可能相当复杂;解决它们需要时间。它会显着增加构建时间,毕竟它不会检测到所有无法访问的语句。编译器旨在采取一些努力,但不会为此花费太多时间。

剩下的唯一问题是:在哪里停止解决条件?其原因似乎没有数学依据,而是基于使用场景。 JLS-14.21

给出了您的特定案例的基本原理

【讨论】:

以上是关于为啥 Java 编译器不会为无法访问的 then 语句生成无法访问的语句错误?的主要内容,如果未能解决你的问题,请参考以下文章

当 if 条件为常量时,为啥未检测到无法访问的代码?

为啥 Java 会出现“无法访问的语句”编译器错误?

代码中显然有一个无法访问的语句,但可以编译 - 为啥? [复制]

java 为啥使用continue会使程序错误

为啥我不能在 firebase 'then' 回调中访问 vuejs 'this'? [复制]

ASP.NET编译成功后,为啥有些功能不会执行?麻烦高手解答!