具有返回类型的Java方法无需返回语句即可编译


228

问题1:

为什么下面的代码在没有返回语句的情况下进行编译?

public int a() {
    while(true);
}

注意:如果我在片刻之后添加return,则会得到Unreachable Code Error

问题2:

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

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

即使以下情况并非如此。

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

2
由于第二个问题的后半部分,所以不是stackoverflow.com/questions/16789832/…的副本。
TJ Crowder

Answers:


274

问题1:

为什么下面的代码在没有返回语句的情况下进行编译?

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节)。

在您的b == b示例中,由于涉及到变量,因此它不是常量表达式,也没有指定在编译时确定。我们可以看到在这种情况下它总是正确的(尽管QBrute 指出如果b是a double,我们很容易被愚弄,这不是它本身),但是JLS仅指定了常量表达式是在编译时确定的,它不允许编译器尝试评估非常数表达式。bayou.io 提出了一个很好的理由,为什么不这样做:如果您开始尝试在编译时确定涉及变量的表达式,那么该在哪里停下来?很明显(对于非Double.NaN==b == bNaN值),那又如何a + b == b + a呢?还是(a + b) * 2 == a * 2 + b * 2?以常数画线很有意义。

因此,由于它不“确定”表达式,因此编译器不知道循环永远不会终止,因此认为该方法可以正常返回-不允许这样做,因为它必须使用return。因此它抱怨缺少一个return


34

它可以是有趣的,没想到一个方法的返回类型为承诺返回指定类型的值,而是一个承诺返回的值是指定类型的。因此,如果您从不退货,那么您就没有兑现承诺,因此以下任何一项都是合法的:

  1. 永远循环:

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

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

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

(我发现递归的考虑很有趣:编译器认为该方法将返回type的值X(无论是哪种值),但事实并非如此,因为不存在任何代码,该代码不知道如何创建或采购X。)


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

注意“ areturn”,意思是“返回参考”

现在,如果我们执行以下操作:

public String getBar() { 
    return 1; 
}

将返回以下字节码:

public String getBar();
  Code:
   0:   iconst_1
   1:   ireturn

现在我们可以看到定义中的类型与ireturn的返回类型不匹配,这意味着return int。

因此,真正的原因在于,如果该方法具有返回路径,则该路径必须与返回类型匹配。但是字节码中有一些实例,根本没有生成返回路径,因此不会破坏规则。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.