Java Enums and Switch Statements - the default case?
For people suggesting throwing an exception:
Throwing an exception doesn't give me a compile-time error, it gives me a runtime error. I know I can throw an exception, I'd rather die during compilation than during runtime.
First-off, I am using eclipse 3.4.
I have a data model that has a mode property that is an Enum.
enum Mode {on(...), off(...), standby(...); ...}
I am currently writing a view of this model and I have the code
...
switch(model.getMode()) {
case on:
return getOnColor();
case off:
return getOffColor();
case standby:
return getStandbyColor();
}
...
I am getting an error "This method must return a result of type java.awt.Color" because I have no default case and no return xxx at the end of the function. I want a compilation error in the case where someone adds another type to the enum (e.g. shuttingdown) so I don't want to put a default case that throws an AssertionError, as this will compile with a modified Mode and not be seen as an error until runtime.
My question is this:
Why does EclipseBuilder (and javac) not recognize that this switch covers all possibilities (or does it cover them?) and stop warning me about needing a return type. Is there a way I can do what I want without adding methods to Mode?
Failing that, is there an option to warn/error on switch statements that don't cover all of the Enum's possible values?
Edit: Rob: It is a compile error. I just tried compiling it with javac and I get a "missing return statement" error targeting the last } of the method. Eclispe just places the error at the top of the method.
Solution 1:
You could always use the Enum with Visitor pattern:
enum Mode {
on {
public <E> E accept( ModeVisitor<E> visitor ) {
return visitor.visitOn();
}
},
off {
public <E> E accept( ModeVisitor<E> visitor ) {
return visitor.visitOff();
}
},
standby {
public <E> E accept( ModeVisitor<E> visitor ) {
return visitor.visitStandby();
}
}
public abstract <E> E accept( ModeVisitor<E> visitor );
public interface ModeVisitor<E> {
E visitOn();
E visitOff();
E visitStandby();
}
}
Then you would implement something like the following:
public final class ModeColorVisitor implements ModeVisitor<Color> {
public Color visitOn() {
return getOnColor();
}
public Color visitOff() {
return getOffColor();
}
public Color visitStandby() {
return getStandbyColor();
}
}
You'd use it as follows:
return model.getMode().accept( new ModeColorVisitor() );
This is a lot more verbose but you'd immediately get a compile error if a new enum was declared.
Solution 2:
You have to enable in Eclipse (window -> preferences) settings "Enum type constant not covered in switch" with Error level.
Throw an exception at the end of the method, but don't use default case.
public String method(Foo foo)
switch(foo) {
case x: return "x";
case y: return "y";
}
throw new IllegalArgumentException();
}
Now if someone adds new case later, Eclipse will make him know he's missing a case. So don't ever use default unless you have really good reasons to do so.
Solution 3:
I don't know why you get this error, but here is a suggestion, Why don't you define the color in the enum itself? Then you can't accidentally forget to define a new color.
For example:
import java.awt.Color;
public class Test {
enum Mode
{
on (Color.BLACK),
off (Color.RED),
standby (Color.GREEN);
private final Color color;
Mode (Color aColor) { color = aColor; }
Color getColor() { return color; }
}
class Model
{
private Mode mode;
public Mode getMode () { return mode; }
}
private Model model;
public Color getColor()
{
return model.getMode().getColor();
}
}
btw, for comparison here is the original case, with compiler error.
import java.awt.Color;
public class Test {
enum Mode {on, off, standby;}
class Model
{
private Mode mode;
public Mode getMode () { return mode; }
}
private Model model;
public Color getColor()
{
switch(model.getMode()) {
case on:
return Color.BLACK;
case off:
return Color.RED;
case standby:
return Color.GREEN;
}
}
}