假设我们有switch语句,它完全涵盖了enum参数的所有可能情况,并且也进行了空检查,不会编译"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
案例或枚举后抛出异常,或访问枚举元素而不是switch
将解决问题。但我不明白这种行为的技术原因:显然,没有可能的执行分支,这不会导致return
或throw
。在某些情况下,编译时检查是否涵盖所有案例都会很棒。
答案 0 :(得分:6)
由于您没有写默认值,编译器会在切换块后的下一行自动添加它。那时,编译器"注意"该方法没有返回点,并给出了该错误。
我已经把你的例子改了,但是在切换后添加了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 Example.class来查看实际的字节码(见下文)。请注意"默认值:52"这是由javac添加的。 它导致切换后的块部分,在那里,我抛出了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
}
答案 1 :(得分:5)
编译器不检查是否已将Foo
中的所有常量列为case
块,从而引发错误。
假设Foo
被定义为:
enum Foo {ONE,TWO,THREE}
然后,如果将Foo.THREE
作为参数传递,您的方法会返回什么?
作为switch
方法的替代方法,您可以在int
枚举中添加Foo
成员,并为每个常量设置相应的数字:
enum Foo {
ONE(1),TWO(2);
int value;
Foo(int value) {
this.value = value;
}
}
这样您就不需要switch
,编译器会请您为任何可能的新Foo
常量设置相应的数字。
答案 2 :(得分:5)
我会在黑暗中拍摄,没有阅读任何理由,但如果这不是行为的主要原因,那么至少< em> a 原因。
假设enum来自您的项目所依赖的库。在您编译的版本中,ONE
和TWO
是唯一的选项。但是,您可能会针对添加了另一个值THREE
的更高版本(通过OSGi或其他解决方案)运行。如果THREE
传递给fooToInt
,它将到达您方法的结尾,并且不会返回(或抛出)任何内容。糟糕。
在运行时发现这是相当不愉快的,所以你不得不选择如何处理它,即使在编译时它实际上似乎是不可能的。某些情况(例如示例中的情况)可能会被检测到并允许编译,而其他情况可能处理不同(例如隐式throw
),但在列表中所有可以用来改进Java的东西,我都不会把它放在最顶层。
答案 3 :(得分:1)
原因是编译器实际上没有通过你的开关来检查你是否已经实现了每一个案例,它只是检查现有案例是否与 Foo - 因此它需要一些东西,默认块或返回。
答案 4 :(得分:-2)
你错过了default
区块。
因为你的方法必须返回一些值。
将返回放在switch语句之外。或者输入默认块