具有返回类型的 Java 方法编译时不带 return 语句

Java method with return type compiles without return statement

提问人:Willi Mentzel 提问时间:6/25/2015 最后编辑:Willi Mentzel 更新时间:12/30/2017 访问量:17531

问:

问题 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);
}
Java 语法 while-loop 编译 返回

评论

2赞 T.J. Crowder 7/2/2015
不是 stackoverflow.com/questions/16789832/ 的重复...,这要归功于第二个问题的后半部分。

答:

275赞 T.J. Crowder 6/25/2015 #1

问题 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?!"); }
}

由于编译器知道循环永远不会终止(当然,它总是为真),它知道函数不能“正常返回”(从其主体的末尾删除),因此没有 .truereturn

问题 2:

另一方面,为什么编译以下代码,

public int a() 
{
    while(0 == 0);
}

即使以下没有。

public int a(int b)
{
    while(b == b);
}

在这种情况下,编译器知道循环永远不会终止(这始终为真)。但它不知道.0 == 00 == 0b == b

为什么不呢?

编译器理解常量表达式 (§15.28)。引用§15.2 - 表达形式(因为奇怪的是这句话不在§15.28中):

某些表达式具有可在编译时确定的值。这些是常量表达式 (§15.28)。

在示例中,由于涉及变量,因此它不是常量表达式,并且未指定在编译时确定。我们可以看到,在这种情况下,它总是正确的(尽管如果是 ,正如 QBrute 指出的那样,我们很容易被 愚弄,它不是 == 本身),但 JLS 只指定常量表达式是在编译时确定的,它不允许编译器尝试计算非常量表达式。bayou.io 提出了一个很好的观点:如果你开始尝试在编译时确定涉及变量的表达式,你在哪里停下来? 是显而易见的(呃,对于非值),但是呢?或?在常量上画线是有道理的。b == bbdoubleDouble.NaNb == bNaNa + b == b + a(a + b) * 2 == a * 2 + b * 2

因此,由于它不“确定”表达式,编译器不知道循环永远不会终止,因此它认为该方法可以正常返回——这是不允许的,因为它需要使用 .所以它抱怨缺少.returnreturn

34赞 Boann 6/26/2015 #2

将方法返回类型视为返回指定类型的值的承诺,而不是不返回非指定类型的值的承诺,可能会很有趣。因此,如果您从不退货,您就没有违背承诺,因此以下任何一项都是合法的:

  1. 永远循环:

    X foo() {
        for (;;);
    }
    
  2. 永远递归:

    X foo() {
        return foo();
    }
    
  3. 抛出异常:

    X foo() {
        throw new Error();
    }
    

(我发现递归很有趣:编译器认为该方法将返回一个类型的值(无论它是什么),但事实并非如此,因为不存在任何代码知道如何创建或获取 .)XX

8赞 Philip Devine 6/27/2015 #3

查看字节码,如果返回的内容与定义不匹配,您将收到编译错误。

例:

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。

因此,归根结底,如果方法具有返回路径,则该路径必须与返回类型匹配。但是字节码中存在根本没有生成返回路径的情况,因此不会违反规则。