问题 1:
为什么下面的代码在没有 return 语句的情况下进行编译?
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
return
问题 2:
另一方面,为什么下面的代码会编译,
public int a()
{
while(0 == 0);
}
即使以下情况并非如此。
public int a(int b)
{
while(b == b);
}
在这种情况下,编译器知道循环永远不会终止(这始终为真)。但是它不知道.0 == 0
0 == 0
b == b
为什么不呢?
编译器理解常量表达式 (§15.28)。引用 §15.2 - 表达式的形式(因为奇怪的是,这句话不在 §15.28 中):
某些表达式具有可在编译时确定的值。这些是常量表达式 (§15.28)。
在您的示例中,由于涉及一个变量,因此它不是常量表达式,并且未指定在编译时确定。我们可以看到,在这种情况下,它总是正确的(尽管如果是一个,正如QBrute指出的那样,我们很容易被愚弄,这不是==
本身),但JLS只指定常量表达式是在编译时确定的,它不允许编译器尝试计算非常量表达式。bayou.io 提出了一个很好的观点:如果你开始在编译时尝试确定涉及变量的表达式,你会在哪里停下来? 是显而易见的(呃,对于非值),但是呢?或?在常量处绘制线条是有意义的。b == b
b
double
Double.NaN
b == b
NaN
a + b == b + a
(a + b) * 2 == a * 2 + b * 2
因此,由于它不“确定”表达式,编译器不知道循环永远不会终止,因此它认为该方法可以正常返回 - 这是不允许的,因为它需要使用。因此,它抱怨缺少.return
return