问题1:
为什么以下代码在没有return语句的情况下编译?
public int a() {
while(true);
}
注意:如果我在一段时间后添加返回,那么我会得到一个Unreachable Code Error
。
问题2:
另一方面,为什么以下代码编译,
public int a() {
while(0 == 0);
}
即使以下情况没有。
public int a(int b) {
while(b == b);
}
答案 0 :(得分:274)
涵盖问题1:
为什么以下代码在没有return语句的情况下编译?
public int a() { while(true); }
如果声明方法具有返回类型(第8.4.5节),则如果方法体可以正常完成(第14.1节),则会发生编译时错误。
换句话说,具有返回类型的方法必须仅使用提供值返回的return语句返回;该方法不允许“掉落其身体的末端”。有关方法体中返回语句的准确规则,请参见§14.17。
方法可能具有返回类型但不包含return语句。这是一个例子:
class DizzyDean { int pitch() { throw new RuntimeException("90 mph?!"); } }
由于编译器知道循环永远不会终止(当然true
总是为真),它知道函数不能“正常返回”(从它的末端掉下来),因此它没关系没有return
。
问题2:
另一方面,为什么以下代码编译,
public int a() { while(0 == 0); }
即使以下情况没有。
public int a(int b) { while(b == b); }
在0 == 0
情况下,编译器知道循环永远不会终止(0 == 0
将永远为真)。但不知道b == b
。
为什么不呢?
编译器理解constant expressions (§15.28)。引用§15.2 - Forms of Expressions (因为奇怪的是这句话不在§15.28中):
某些表达式具有可在编译时确定的值。这些是常量表达式(§15.28)。
在b == b
示例中,因为涉及变量,所以它不是常量表达式,并且未指定在编译时确定。 我们可以看到在这种情况下总是如此(尽管如果b
是double
,而QBrute pointed out,我们很容易被愚弄Double.NaN
,not ==
itself),但JLS仅指定在编译时确定常量表达式,它不允许编译器尝试计算非常量表达式。 bayou.io raised a good point为什么不:如果你开始在编译时试图确定涉及变量的表达式,你会在哪里停止? b == b
显而易见(呃,对于非NaN
值),但是a + b == b + a
呢?还是(a + b) * 2 == a * 2 + b * 2
?在常数处绘制线条是有道理的。
因为它没有“确定”表达式,所以编译器不知道循环永远不会终止,因此它认为该方法可以正常返回 - 这是不允许的,因为它需要使用{ {1}}。所以它抱怨缺少return
。
答案 1 :(得分:33)
将方法返回类型视为返回指定类型的值的承诺,但作为承诺而不是返回不<的值,可能会很有趣指定类型的/ em>。因此,如果你永远不会退货,你就不会违背承诺,所以以下任何一种都是合法的:
永远循环:
X foo() {
for (;;);
}
永远递归:
X foo() {
return foo();
}
抛出异常:
X foo() {
throw new Error();
}
(我发现递归是一个有趣的思考:编译器认为该方法将返回类型X
的值(无论是什么),但它不是真的,因为没有代码存在,知道如何创建或获得X
。)
答案 2 :(得分:8)
查看字节代码,如果返回的内容与定义不匹配,则会收到编译错误。
示例:强>
for(;;)
将显示字节码:
L0
LINENUMBER 6 L0
FRAME SAME
GOTO L0
注意缺少任何返回字节码
这不会返回,因此不会返回错误的类型。
为了比较,可以采用以下方法:
public String getBar() {
return bar;
}
将返回以下字节码:
public java.lang.String getBar();
Code:
0: aload_0
1: getfield #2; //Field bar:Ljava/lang/String;
4: areturn
注意&#34; beturn&#34;这意味着&#34;返回参考&#34;
现在,如果我们执行以下操作:
public String getBar() {
return 1;
}
将返回以下字节码:
public String getBar();
Code:
0: iconst_1
1: ireturn
现在我们可以看到定义中的类型与ireturn的返回类型不匹配,这意味着返回int。
所以它真正归结为如果该方法具有返回路径,则该路径必须与返回类型匹配。但是在字节码中有些实例根本没有生成返回路径,因此没有违反规则。