【问题标题】:Java method with return type compiles without return statement具有返回类型的 Java 方法在没有返回语句的情况下编译
【发布时间】:2015-09-12 01:06:29
【问题描述】:

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

【问题讨论】:

标签: java syntax while-loop compilation return


【解决方案1】:

将方法返回类型不作为返回指定类型值的承诺,而是作为返回值的承诺可能会很有趣/em> 的指定类型。因此,如果您从不返回任何东西,您就没有违反承诺,因此以下任何一项都是合法的:

  1. 永远循环:

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

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

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

(我觉得考虑递归很有趣:编译器认为该方法将返回 X 类型的值(不管是什么),但事实并非如此,因为不存在具有知道如何创建或获取X。)

【讨论】:

    【解决方案2】:

    问题 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

    为什么不呢?

    编译器理解constant expressions (§15.28)。引用§15.2 - Forms of Expressions (因为奇怪的是这句话不在 §15.28 中)

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

    在您的b == b 示例中,因为涉及到一个变量,所以它不是一个常量表达式,也没有指定在编译时确定。 我们可以看到在这种情况下它总是正确的(尽管如果 bdouble,作为 QBrute pointed out,我们很容易被 Double.NaN 愚弄,这是not == itself),但 JLS 仅指定常量表达式在编译时确定,它不允许编译器尝试评估非常量表达式。 bayou.io raised a good point 为什么不这样做:如果你开始尝试在编译时确定涉及变量的表达式,你会在哪里停下来? b == b 很明显(呃,对于非NaN 值),但是a + b == b + a 呢?还是(a + b) * 2 == a * 2 + b * 2?在常数处画线是有意义的。

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

    【讨论】:

      【解决方案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
      

      注意“return”,意思是“返回引用”

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

      public String getBar() { 
          return 1; 
      }
      

      将返回以下字节码:

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

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

      所以真正归结为如果方法有返回路径,则该路径必须与返回类型匹配。但是在字节码中有一些实例根本没有生成返回路径,因此没有违反规则。

      【讨论】:

        猜你喜欢
        • 2013-05-29
        • 1970-01-01
        • 2011-02-11
        • 1970-01-01
        • 1970-01-01
        • 2012-01-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多