问题一:
为什么下面的代码编译时没有返回语句?
public int a() {
while(true);
}
注意:如果我在一段时间后添加 return,那么我会得到一个 Unreachable Code Error
。
问题2:
另一方面,为什么下面的代码会编译,
public int a() {
while(0 == 0);
}
即使以下没有。
public int a(int b) {
while(b == b);
}
问题一:为什么下面的代码编译时没有返回语句? public int a() { while(true); }
JLS§8.4.7 涵盖了这一点:
如果将方法声明为具有返回类型(第 8.4.5 节),则如果方法的主体可以正常完成(第 14.1 节),则会发生编译时错误。换句话说,具有返回类型的方法必须仅通过使用提供值返回的 return 语句返回;该方法不允许“丢弃其主体的末端”。有关方法体中的 return 语句的精确规则,请参见第 14.17 节。一个方法可能有一个返回类型但不包含返回语句。这是一个例子: class DizzyDean { int pitch() { throw new RuntimeException("90 mph?!"); } }
由于编译器知道循环永远不会终止(当然,true
总是正确的),它知道函数不能“正常返回”(丢弃其主体的末尾),因此没有 {2 }。
问题2:另一方面,为什么下面的代码会编译, public int a() { while(0 == 0);即使以下没有。公共 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
?以常数画线是有道理的。
因此,由于它没有“确定”表达式,编译器不知道循环永远不会终止,因此它认为该方法可以正常返回 ——这是不允许的,因为它需要使用 return
.所以它抱怨缺少 return
。
将方法返回类型不视为返回指定类型值的承诺,而是视为不返回非指定类型值的承诺可能会很有趣。因此,如果你从不返回任何东西,你就没有违背承诺,因此以下任何一项都是合法的:
永远循环: X foo() { for (;;); } 永远递归: X foo() { return foo(); } 抛出异常: X foo() { throw new Error(); }
(我发现考虑递归很有趣:编译器认为该方法将返回 X
类型的值(无论是什么),但事实并非如此,因为没有代码知道如何创建或获取 X
。)
查看字节码,如果返回的内容与定义不匹配,您将收到编译错误。
例子:
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
注意“return”,意思是“返回参考”
现在,如果我们执行以下操作:
public String getBar() {
return 1;
}
将返回以下字节码:
public String getBar();
Code:
0: iconst_1
1: ireturn
现在我们可以看到定义中的类型与ireturn的返回类型不匹配,即return int。
所以真正归结为,如果方法有返回路径,则该路径必须与返回类型匹配。但是在字节码中有一些实例根本没有生成返回路径,因此没有违反规则。