Java案例的具体细节(可能与C#案例非常相似)与Java编译器如何确定方法是否能够返回有关。
具体来说,规则是根据JLS 8.4.7,具有返回类型的方法必须不能正常完成,而必须始终突然完成(此处通过返回语句或异常指示)。
如果声明某个方法具有返回类型,则如果该方法的主体可以正常完成,则会发生编译时错误。换句话说,具有返回类型的方法只能通过使用提供值return的return语句来返回;不允许“掉落尸体的末端”。
编译器将根据JLS 14.21 Unreachable Statements中定义的规则来查看是否可以正常终止,因为它还定义了正常完成的规则。
值得注意的是,不可达语句的规则仅对具有已定义true
常量表达式的循环具有特殊情况:
如果满足以下至少一项条件,则while语句可以正常完成:
因此,如果该while
语句可以正常完成,则下面的return语句是必需的,因为该代码被认为是可到达的,并且while
没有可到达的break语句或常量true
表达式的任何循环都被视为能够正常完成。
这些规则意味着你while
有一个恒定的真实意思表示,没有一份声明break
中从来没有考虑过正常完成,因此它下面的任何代码永远不会认为是可达的。该方法的结尾在循环之下,并且由于该循环之下的所有内容均不可访问,因此该方法的结尾也是如此,因此该方法可能无法正常完成(这是编译器所寻找的)。
if
另一方面,对于循环提供的常量表达式,语句没有特殊的豁免。
比较:
// I have a compiler error!
public boolean testReturn()
{
final boolean condition = true;
if (condition) return true;
}
带有:
// I compile just fine!
public boolean testReturn()
{
final boolean condition = true;
while (condition)
{
return true;
}
}
区别的原因非常有趣,并且是由于希望允许不会导致编译器错误的条件编译标志(来自JLS):
可能希望if语句以下列方式处理:
如果至少满足以下条件之一,则if-then语句可以正常完成:
如果if-then语句是可到达的,并且条件表达式不是其值为false的常数表达式,则then语句是可到达的。
如果then语句可以正常完成或else语句可以正常完成,则if-then-else语句可以正常完成。
这种方法将与其他控制结构的处理一致。但是,为了允许if语句方便地用于“条件编译”目的,实际规则有所不同。
例如,以下语句导致编译时错误:
while (false) { x=3; }
因为该陈述x=3;
是无法达到的;但表面上类似的情况:
if (false) { x=3; }
不会导致编译时错误。优化的编译器可能会意识到该语句x=3;
将永远不会执行,并且可以选择从生成的类文件中省略该语句的代码,但是从x=3;
此处指定的技术意义上讲,该语句不被视为“不可访问的”。
这种不同处理的原理是允许程序员定义“标志变量”,例如:
static final boolean DEBUG = false;
然后编写如下代码:
if (DEBUG) { x=3; }
这个想法是应该可以将DEBUG的值从false更改为true或从true更改为false,然后正确编译代码,而无需对程序文本进行其他更改。
为什么条件break语句会导致编译器错误?
如循环可达性规则中所述,如果while循环包含可到达的break语句,则while循环也可以正常完成。由于if
语句的then子句的可到达性规则根本没有考虑条件,因此始终if
将条件if
语句的then子句视为可到达。
如果break
可以到达,则循环后的代码也再次被视为可到达。由于没有可访问的代码导致循环后突然终止,因此该方法被认为能够正常完成,因此编译器将其标记为错误。