有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

在Java中完全打开枚举会产生“缺少返回语句”错误

假设,我们有一个switch语句,它完全覆盖了枚举参数的所有可能情况,并且也有空检查,由于"Missing return statement"的原因,它不会被编译

enum Foo {ONE,TWO}

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

我知道,从default案例或在enum之后抛出异常,或者访问enum元素而不是switch将解决问题。但我不理解这种行为的技术原因:显然,不存在不导致returnthrow的可能的执行分支。此外,在某些情况下,最好在编译时检查是否涵盖了所有情况


共 (5) 个答案

  1. # 1 楼答案

    你错过了default

    因为你的方法必须返回一些值

    return置于switch语句之外。或放置默认值

  2. # 2 楼答案

    在这里我将在黑暗中试一试,没有读到任何理由,但如果这不是行为的主要原因,至少是一个原因

    假设enum来自项目所依赖的库。在您编译的版本中,ONETWO是唯一的选项。但是,您可能会在一个更高版本(通过OSGi或其他解决方案)上运行,该版本增加了另一个价值^{)。如果THREE被传递到fooToInt,它将到达方法的末尾,并且不会返回(或抛出)任何内容。哎呀

    这在运行时很难发现,因此您不得不选择如何处理它,即使在编译时它实际上看起来不可能。有些情况,比如您的示例中的情况,可能会被检测到并允许编译,而另一些情况可能会得到不同的处理(例如隐式的^{),但在所有可以改进Java的事情列表中,我不会把它放在首位

  3. # 3 楼答案

    原因是编译器实际上并没有通过你的开关来检查你是否实现了每一个case,它只是检查现有的case是否与Foo中的实际值相对应——因此它需要一些东西,要么是默认块,要么是返回

  4. # 4 楼答案

    由于没有编写默认值,编译器会在开关块后的下一行自动添加它。此时,编译器会“注意”该方法没有返回点,并给出该错误

    我以您为例进行了更改,但在切换后添加了RuntimeException抛出,如下所示:

    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示例。类以查看实际字节码(请参见下文)。注意javac添加的“default:52”。 它将引导到switch case之后的block部分,在那里,我抛出了RuntimeException,它覆盖了返回的需要

    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
    }
    
  5. # 5 楼答案

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

    假设Foo被定义为:

    enum Foo {ONE,TWO,THREE}
    

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


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

    enum Foo {
        ONE(1),TWO(2);
    
        int value;
        Foo(int value) {
            this.value = value;
        }
    }
    

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