提问人:Willi Mentzel 提问时间:6/25/2015 最后编辑:Willi Mentzel 更新时间:12/30/2017 访问量:17531
具有返回类型的 Java 方法编译时不带 return 语句
Java method with return type compiles without return statement
问:
问题 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);
}
答:
问题 1:
为什么下面的代码在没有 return 语句的情况下编译?
public int a() { while(true); }
JLS§8.4.7 对此进行了介绍:
如果将方法声明为具有返回类型 (§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
为什么不呢?
编译器理解常量表达式 (§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
将方法返回类型视为不返回指定类型的值的承诺,而不是不返回非指定类型的值的承诺,可能会很有趣。因此,如果您从不退货,您就没有违背承诺,因此以下任何一项都是合法的:
永远循环:
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
请注意“areturn”,意思是“返回引用”
现在,如果我们执行以下操作:
public String getBar() {
return 1;
}
将返回以下字节码:
public String getBar();
Code:
0: iconst_1
1: ireturn
现在我们可以看到定义中的类型与 ireturn 的返回类型不匹配,这意味着返回 int。
因此,归根结底,如果方法具有返回路径,则该路径必须与返回类型匹配。但是字节码中存在根本没有生成返回路径的情况,因此不会违反规则。
评论