首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Java中完全切换枚举会产生"Missing return statement“错误

在Java中完全切换枚举会产生"Missing return statement“错误
EN

Stack Overflow用户
提问于 2016-08-30 18:52:53
回答 5查看 4.4K关注 0票数 9

假设我们有switch语句,它完全覆盖了枚举参数的所有可能的情况,并且也有null-check,将不会因为"Missing return statement"而被编译。

代码语言:javascript
复制
enum Foo {ONE,TWO}

int fooToInt(Foo foo) {
    if (foo == null) {
        throw new NullPointerException();
    }
    switch (foo) {
        case ONE: return 1;
        case TWO: return 2;
    }
}

我知道,在枚举情况下或在枚举之后抛出异常,或者访问枚举元素而不是switch可以解决这个问题。但我不理解这种行为的技术原因:显然,没有可能的执行分支,这不会导致returnthrow。此外,在某些情况下,能够在编译时检查是否涵盖了所有情况也是很好的。

EN

回答 5

Stack Overflow用户

发布于 2017-05-31 01:16:08

我没有读过任何关于这一点的理论基础,在这里我要试一试,但如果这不是这种行为的主要原因,它至少是一个原因。

假设枚举来自您的项目所依赖的库。在您编译的版本中,ONETWO是唯一的选项。但是,您可能最终运行的是添加了另一个值THREE的较新版本(通过OSGi或其他解决方案)。如果THREE被传递给fooToInt,它将到达您的方法的末尾,并且不会返回(或抛出)任何东西。糟了。

在运行时发现这是相当令人不快的,所以你不得不选择如何处理它,即使它在编译时看起来是不可能的。有些情况,像你的例子中的情况,可能可以被检测到并允许编译,而其他情况可能会有不同的处理方式(例如,隐式throw),但在所有可以改进Java的事情的列表中,我不会把它放在首位。

票数 9
EN

Stack Overflow用户

发布于 2016-08-30 19:42:03

由于您没有编写default,编译器会在switch块之后的下一行自动添加它。在这一点上,编译器“注意到”该方法没有返回点,并给出了这个错误。

我以您为例,并在切换后添加了RuntimeException代码,如下所示:

代码语言:javascript
复制
public class Example {

   enum Foo { ONE, TWO }

    int fooToInt(Foo foo) {
        if (foo == null) {
            throw new NullPointerException();
        }
        switch (foo) {
            case ONE: return 1;
            case TWO: return 2;
        }

        throw new RuntimeException("Should not have gotten here");
    }

    public static void main(String[] args) {

    }
}

我已经编译了这个类并使用了javap -c Example.class来查看实际的字节码(见下文)。注意javac添加的"default: 52“。它指向切换案例之后的块部分,在那里,我抛出了RuntimeException,它覆盖了返回的需要。

代码语言:javascript
复制
Compiled from "Example.java"
public class com.mprv.automation.jenkins.Example {
  public com.mprv.automation.jenkins.Example();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  int fooToInt(com.mprv.automation.jenkins.Example$Foo);
    Code:
       0: aload_1
       1: ifnonnull     12
       4: new           #2                  // class java/lang/NullPointerException
       7: dup
       8: invokespecial #3                  // Method java/lang/NullPointerException."<init>":()V
      11: athrow
      12: getstatic     #4                  // Field com/mprv/automation/jenkins/Example$1.$SwitchMap$com$mprv$automation$jenkins$Example$Foo:[I
      15: aload_1
      16: invokevirtual #5                  // Method com/mprv/automation/jenkins/Example$Foo.ordinal:()I
      19: iaload
      20: lookupswitch  { // 2
                     1: 48
                     2: 50
               default: 52
          }
      48: iconst_1
      49: ireturn
      50: iconst_2
      51: ireturn
      52: new           #6                  // class java/lang/RuntimeException
      55: dup
      56: ldc           #7                  // String Should not have gotten here
      58: invokespecial #8                  // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
      61: athrow

  public static void main(java.lang.String[]);
    Code:
       0: return
}
票数 8
EN

Stack Overflow用户

发布于 2016-08-30 18:55:04

编译器不会检查您是否将Foo中的所有常量都作为case块列出,从而引发和错误。

假设Foo被定义为:

代码语言:javascript
复制
enum Foo {ONE,TWO,THREE}

那么,如果您将Foo.THREE作为参数传递,您的方法将返回什么?

作为switch方法的替代方法,您可以在Foo枚举中添加一个int成员,并为每个常量设置相应的数字:

代码语言:javascript
复制
enum Foo {
    ONE(1),TWO(2);

    int value;
    Foo(int value) {
        this.value = value;
    }
}

这样你就不需要switch了,编译器会亲切地要求你为任何可能的新Foo常量设置一个相应的数字。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39225553

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档