具有返回types的Java方法编译时没有返回语句

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

问题1:

为什么下面的代码没有返回语句编译?

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

这包含在JLS§8.4.7中 :

如果一个方法被声明为返回types(§8.4.5),那么如果方法的主体可以正常完成(§14.1),则会发生编译时错误。

换句话说,具有返回types的方法只能通过使用提供返回值的返回语句来返回; 该方法不允许“脱身”。 关于方法体中的return语句的精确规则,请参阅§14.17。

一个方法可能有一个返回types,但不包含返回语句。 这里是一个例子:

 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

为什么不?

编译器理解常量expression式(第15.28节) 。 引用§15.2 – expressionforms (因为奇怪的是这句话不在§15.28中)

有些expression式的值可以在编译时确定。 这些是常量expression式 (§15.28)。

在你的b == b例子中,因为涉及到一个variables,所以它不是一个常量expression式,并且在编译时没有被指定。 我们可以看到,在这种情况下总是会是真的(尽pipe如果bdouble ,就像QBrute 指出的那样 ,我们很容易被Double.NaN愚弄,而不是==本身 ),但是JLS只指定常量expression式在编译时确定,它不允许编译器尝试计算非常量expression式。 bayou.io 提出了一个很好的观点 ,为什么不呢:如果你开始在编译时试图确定涉及variables的expression式,那么你停在哪里? b == b很明显(呃,对于非NaN值),但是a + b == b + a呢? 或(a + b) * 2 == a * 2 + b * 2 ? 在常量上画线是有意义的。

因此,由于它不“确定”expression式,编译器不知道循环将永远不会终止,所以它认为该方法可以正常返回 – 这是不允许的,因为它需要使用return 。 所以它抱怨没有return

认为方法返回types不是返回指定types的值的承诺,而是承诺不返回指定types的值。 因此,如果你永远不会退还任何东西,那么你就没有违背承诺,所以下列任何一条都是合法的:

  1. 永远循环:

     X foo() { for (;;); } 
  2. 永久recursion:

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

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

(由于这些方法实际上不会返回值,因此X的types并不重要。)

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

例:

for(;;)将显示字节码:

 L0 LINENUMBER 6 L0 FRAME SAME GOTO L0 

注意缺less任何返回字节码

这并不是一个回报,因此不会返回错误的types。

为了比较,一种方法是:

 public String getBar() { return bar; } 

将返回以下字节码:

 public java.lang.String getBar(); Code: 0: aload_0 1: getfield #2; //Field bar:Ljava/lang/String; 4: areturn 

注意“返回”意味着“返回一个参考”

现在,如果我们做到以下几点:

 public String getBar() { return 1; } 

将返回以下字节码:

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

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

所以真正的结果是,如果方法有返回path,该path必须匹配返回types。 但是在字节码中有一些实例根本没有返回path,因此没有违规。