当交换机用于枚举时,默认的用法是什么?

假设我有一个枚举Color与2个可能的值: REDBLUE

 public enum Color { RED, BLUE } 

现在假设我有一个关于这个枚举的switch语句,我有两个可能的值的代码:

 Color color = getColor(); // a method which returns a value of enum "Color" switch (color) { case RED: ... break; case BLUE: ... break; default: break; } 

由于我有两个可能的枚举值的代码块,在上面的代码中default的用法是什么?

如果代码以某种方式达到这样的default块,我应该抛出一个exception吗?

 Color color = getColor(); // a method which returns a value of enum "Color" switch (color) { case RED: ... break; case BLUE: ... break; default: throw new IllegalArgumentException("This should not have happened"); } 

如第二个例子所示,抛出exception是一个好习惯。 通过快速失败来提高代码的可维护性。

在这种情况下,这意味着如果你稍后(也许几年之后)添加一个枚举值,并且它到达switch语句,您将立即发现错误。

如果没有设置默认值,代码甚至可能通过新的枚举值运行,并可能有不受欢迎的行为。

其他的答案是正确的,说你应该实现一个default分支,抛出一个exception,以防万一有新值被添加到你的枚举中。 但是,我会更进一步,质疑为什么你甚至首先使用switch语句。

与C ++和C#等语言不同,Java将Enum值表示为实际对象,这意味着您可以利用面向对象的编程。 假设您的方法的目的是为每种颜色提供一个RGB值:

 switch (color) case RED: return "#ff0000"; ... 

好吧,可以说,如果你想让每种颜色都有一个RGB值,你应该把它作为其描述的一部分:

 public enum Color { RED("#FF0000"), BLUE("#0000FF"); String rgb; public Color(String rgb) { this.rgb = rgb; } public getRgb() { return this.rgb; } } 

这样,如果稍后添加新颜色,则几乎不得不提供RGB值。 这比其他方法更加快速,因为在编译时而不是运行时会失败。

请注意,如果需要,您可以做更复杂的事情,包括让每种颜色提供自己的抽象方法的自定义实现。 Java中的枚举实际上是强大的和面向对象的,在大多数情况下,我发现我可以避免需要首先switch它们。

编译时间完整的开关情况不保证运行时完成。

用旧版枚举编译的switch语句的类可能会用更新的枚举版本(具有更多值)执行。 这是库依赖关系的常见情况。

由于这样的原因,编译器认为switch没有default情况下不完整。

在小程序中,没有实际的用处,但是考虑一个复杂的系统,在大量的文件和开发人员之间进行交stream – 如果你在一个文件中定义enum并在另一个文件中使用它,然后在某个人添加一个值到enum没有更新switch语句,你会发现它非常有用…

是的,你应该这样做。 您可以更改enum但不要更改switch 。 将来会导致错误。 我认为throw new IllegalArgumentException(msg)是一个好习惯。

如果您已经涵盖了各种cases所有可能性,并且default不会发生,那么这就是断言的经典用例:

 Color color = getColor(); // a method which returns a value of enum "Color" switch (color) { case RED: // ... break; case BLUE: // ... break; default: assert false; // This cannot happen // or: throw new AssertionError("Invalid Colors enum"); } 

当枚举常量太多,你只需要处理less数情况下,然后default将处理其余的常量。

此外,枚举常量是引用,如果引用尚未设置,或为null 。 你也可能需要处理这种情况。

是的,这是死代码,直到有人为枚举值添加一个值,这将使您的switch语句遵循“快速失败”( https://en.wikipedia.org/wiki/Fail-fast )原则。

这可能涉及到这个问题: 如何确保在编译时枚举开关的完整性?

为了满足IDE和其他静态短语,我经常把默认情况作为一个空操作,同时还有一个注释,例如// Can't happen// Unreachable

也就是说,如果开关正在处理所有可能的枚举值的典型事件,无论是明确的或通过跌落,那么默认情况下可能是程序员错误。

根据应用程序的不同,我有时会在开发过程中提出一个断言来防止程序员的错误。 但是,这在运输代码中的价值是有限的(除非你提供断言启用。)

再一次,根据情况,我可能会相信抛出一个错误,因为这实际上是一个不可恢复的情况 – 用户可以做的任何事情都不会纠正什么是程序员错误。

除了许多人指出的枚举的将来可能的扩展之外,有一天某个人可能会“改善” getColor()或者在派生类中重写它,并让它返回一个无效值。 当然,编译器应该捕捉到,除非有人明确强制不安全的types转换…

但不好的事情发生了,最好不要留下任何意想不到的elsedefaultpath。

我很惊讶没有人提到这一点。 您可以将一个int转换为一个枚举,并且不会因为该值不是枚举值之一而抛出。 这意味着(除其他外),编译器不能告诉所有的枚举值在交换机中。

即使您正确编写代码,在序列化包含枚举的对象时也会出现这种情况。 未来的版本可能会增加到枚举,你的代码扼杀读取它,或有人打算创造混乱可能hexedit一个新的价值。无论哪种方式,stream失开关很less做正确的事情。 所以,除非我们知道更好,否则我们会抛出违约。

下面是我将如何处理它,除了NULL值,这将导致您可以处理的空指针exception。

如果“ Color color不为null ,则必须是enum Color中的单个元素之一,如果将任何引用分配给不是其中之一的对象,则会导致运行时错误。

所以我的解决scheme是考虑到不支持的值。

testing运行

Test.java

 public Test { public static void main (String [] args) { try { test_1(null); } catch (NullPointerException e) { System.out.println ("NullPointerException"); } try { test_2(null); } catch (Exception e) { System.out.println(e.getMessage()); } try { test_1(Color.Green); } catch (Exception e) { System.out.println(e.getMessage()); } } public static String test_1 (Color color) throws Exception { String out = ""; switch (color) // NullPointerException expected { case Color.Red: out = Red.getName(); break; case Color.Blue: out = Red.getName(); break; default: throw new UnsupportedArgumentException ("unsupported color: " + color.getName()); } return out; } 

..或者你也可以认为null是不支持的

  public static String test_2 (Color color) throws Exception { if (color == null) throw new UnsupportedArgumentException ("unsupported color: NULL"); return test_1(color); } } 

Color.java

 enum Color { Red("Red"), Blue("Blue"), Green("Green"); private final String name; private Color(String n) { name = n; } public String getName() { return name; } } 

UnsupportedArgumentException.java

 class UnsupportedArgumentException extends Exception { private String message = null; public UnsupportedArgumentException() { super(); } public UnsupportedArgumentException (String message) { super(message); this.message = message; } public UnsupportedArgumentException (Throwable cause) { super(cause); } @Override public String toString() { return message; } @Override public String getMessage() { return message; } } 

在这种情况下,默认情况下使用断言是最好的做法。