What is the use of the default when the switch is for enumeration?

Suppose I have a Color enumeration with two possible values: RED and BLUE :

 public enum Color { RED, BLUE } 

Now suppose I have a switch statement for this enumeration, where I have code for both of the possible values:

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

Since I have a code block for both possible enumeration values, what is the use of default in the above code?

Should I make an exception if the code somehow reaches the default block, like this?

 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"); } 
+56
java enums
Oct 08 '15 at 15:06
source share
13 answers

It is good practice to throw an exception, as you showed in the second example. You improve maintainability of your code by quickly refusing.

In this case, this would mean that if you later (perhaps years later) add the enum value and reach the switch statement, you will immediately find an error.

If the default value has not been set, it is possible that the code will work even with the new enumeration value and may have undesirable behavior.

+79
Oct 08 '15 at 15:11
source share

Other answers are correct, saying that you should implement a default branch that throws an exception in case a new value is added to your enumeration in the future. However, I would like to take one more step and ask why you even use the switch .

Unlike languages ​​like C ++ and C #, Java represents Enum values ​​as real objects, which means you can use object-oriented programming. Let's say that the goal of your method is to provide an RGB value for each color:

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

Well, perhaps if you want each color to be RGB, you should include this as part of your description:

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

Thus, if you add a new color later, you are largely forced to provide an RGB value. This is even more unsuccessful than the other approach, because you will fail at compile time and not at runtime.

Note that if you need to do even more complex things, including the presence of each color, its own implementation of the abstract method. Enumerations in Java are really powerful and object oriented, and in most cases I have found that in the first place I can avoid the need for a switch .

+52
Oct 08 '15 at 16:09
source share

The complete compilation of switch enclosures does not guarantee completion.

A class with a switch statement compiled with an older version of the enum can be executed with the new version of enum (with a large number of values). This is a common case with library dependencies.

For reasons like these, the compiler considers the switch case without default incomplete.

+13
Oct 08 '15 at 15:14
source share

Small programs have no practical use for this, but think of a complex system that copies a large number of files and developers - if you define enum in one file and use it in another, then someone adds the value to enum without updating the switch , you will find it very useful ...

+8
08 Oct '15 at 15:10
source share

Yes, you must do it. You can change the enum , but do not change the switch . In the future, this will lead to errors. I believe throw new IllegalArgumentException(msg) is good practice.

+6
Oct 08 '15 at 15:11
source share

If you have covered all the possibilities with the help of various cases and default cannot be, this is a classic use case of statements :

 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"); } 
+6
Oct 09 '15 at 8:18
source share

When there are too many enumeration constants, and you only need to handle a few cases, then default will handle the rest of the constants.

In addition, enumeration constants are links, if the link is not already set, or null . You may need to handle such cases.

+5
Oct 08 '15 at 15:10
source share

To satisfy the IDE and other static linters, I often leave the default case as a no-op along with a comment like // Can't happen or // Unreachable

ie, if the switch performs the typical task of processing all possible enumeration values, either explicitly or through failures, then the default case is probably a programmer's mistake.

Depending on the application, I sometimes make a statement in the case to protect against a programmer’s error during development. But this has limited meaning in the transport code (unless you send messages with claims included.)

Again, depending on the situation, I could convince you of a mistake, since this is really a fatal situation - nothing that the user can do will correct what is probably a programmer's error.

+5
Oct 08 '15 at 15:10
source share

Yes, this is dead code until someone adds a value to the enumeration that makes your switch statement follow the fail fail principle ( https://en.wikipedia.org/wiki/Fail-fast )

This may be due to this question: How to ensure completeness in the enumeration switch at compile time?

+5
Oct 08 '15 at 15:12
source share

Besides the possible future extension of the enumeration, which many have talked about, someone can "improve" yout getColor() or override it in a derived class and let it return an invalid value. Of course, the compiler should catch this unless someone explicitly starts unsafe type casting ...

But bad things just happen, and it's good practice not to leave the unexpected else or default path unguarded.

+3
Oct 09 '15 at 14:06
source share

I am surprised that no one has mentioned this. You can convert an int to an enumeration, and it will not be thrown just because the value is not one of the listed values. This means, by the way, the compiler cannot say that all the enumeration values ​​are in the switch.

Even if you write your code correctly, it does occur when serializing objects containing enumerations. A future version may add to the enumeration, and your code throttle when reading it back, or someone who wants to create chaos, may have the hexadecimal value of the new value. In any case, a failure from the switch rarely does the right thing. Thus, we default if we do not know better.

+3
Oct 09 '15 at 15:16
source share

This is how I would handle this, besides the NULL value, which will result in a null pointer exception that you can handle.

If Color color not NULL , it must be one of the singletones in enum Color , if you assign any reference to an object that is not one of them, this will lead to a runtime error.

So my solution is to consider values ​​that are not supported.

Test run

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; } 

.. or you can consider NULL as unsupported too

  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; } } 
+1
Oct 14 '15 at 10:36
source share

In this case, using Assertion by default is best practice.

0
Oct 23 '16 at 8:12
source share



All Articles