What is a good "unreachable" Error for switches? - java

Eclipse forces me to use a default case for any switch including those listing all declared enum values, allegedly because of the language specification [1]. This is unfortunate because Android Studio, in which the project is developed in parallel, does not, and would naturally warn me about all switches which become incomplete if ever the enum is changed. While I would prefer the latter behaviour because the former makes enum changes actually more error-prone (see example below), I am in no position to choose, so I need to find how to do this right for both. I suppose if there is a place in a code which should under any circumstances remain unreachable but still removing the line is not an option, throwing an Error seems like the natural thing to do there. But which one, is there a generally accepted subclass for such a scenario (perhaps extending to other "forced" unreachable places)? Or is it acceptable to simply throw a quick-and-dirty new Error("enum name") just for the sake of it, instead of writing my own just to be never used?
In the example:
public static enum Color {
RED,
GREEN,
BLUE;
#Override
public String toString() {
switch(this) {
case RED:
return "Red";
case GREEN:
return "Green";
case BLUE:
return "Blue";
default:
/* never reached */
throw new ThisCanNeverHappenError();
}
}
}
adding WHITE to the enum makes this switch and possibly many more throughout the code silent sources of nasty errors as flow control finds them to be just fine.

There was a LONG discussion of this in the Eclipse Bugzilla/Bug 374605.
The end result is that this is a configurable warning and can be disabled.
Change the dropdown from Warning to Ignore

You should not throw an Error. A better exception should be IllegalStateException:
switch(this) {
case RED:
return "Red";
case GREEN:
return "Green";
case BLUE:
return "Blue";
default:
throw new IllegalStateException("Unexpected enum value: " + this.name());
}
On a different note, you shouldn't use a switch statement there anyway. Add a field to the enum. Also note that enums are always static, so you can remove that keyword.
public enum Color {
RED ("Red"),
GREEN("Green"),
BLUE ("Blue");
private final String displayText;
private Color(String displayText) {
this.displayText = displayText;
}
public String getDisplayText() {
return this.displayText;
}
#Override
public String toString() {
return this.displayText;
}
}

There is no need for this switch (and the corresponding handling of the default case) if you add the "toString" value as a parameter of the enum instances:
public static enum Color {
RED("Red"),
GREEN("Green"),
BLUE("Blue");
private final String name;
private Color(String name) {
this.name = name;
}
#Override
public String toString() {
return name;
}
}

Throwing Errors is not a very wise move - most exception-handling code passes them thru, they end up in strange places, might make your process unusable.
I would recommend IllegalStateException.

You are making an assertion: /* never reached */ - if that assertion is erroneous you should probably throw an AssertionError.
The alternative recommendation of throwing an IllegalStateException is not really appropriate - according to the javadoc:
Signals that a method has been invoked at an illegal or inappropriate time
which is not really the case here.

Related

How to test default value in enum using mockito and testNg

I have a third party enum class like this:
package se.api;
public enum InvestigationResultStatus {
PENDING("PendingResult.rr"),
NEW("NewResult.rr"),
MODIFIED("ModifiedResult.rr"),
FINAL("FinalResult.rr"),
PRELIMINARY("PreliminaryResult.rr"),
ADDITIONAL("AdditionalResult.rr"),
REJECTED("RejectedResult.rr"),
COMPLETE("Path.Results.InvestigationInformation.Complete"),
UNDEFINED("");
private final String languageKey;
private InvestigationResultStatus(String languageKey) {
this.languageKey = languageKey;
}
public String getLanguageKey() {
return this.languageKey;
}
}
and in my code I create enum for this class
package se.dto;
public enum InvestigationResultStatus
{
PENDING,
NEW,
MODIFIED,
FINAL,
PRELIMINARY,
ADDITIONAL,
REJECTED,
UNDEFINED
}
to map these api enum to my enum i created enum mapper:
package se.mapper;
import se.dto.InvestigationResultStatus;
public class InvestigationResultStatusMapper
{
public InvestigationResultStatus mapOmResultStatusToNovaResultStatus(final se.api.InvestigationResultStatus investigationResultStatus){
switch (investigationResultStatus){
case PENDING:
return InvestigationResultStatus.PENDING;
case NEW:
return InvestigationResultStatus.NEW;
case MODIFIED:
return InvestigationResultStatus.MODIFIED;
case FINAL:
return InvestigationResultStatus.FINAL;
case PRELIMINARY:
return InvestigationResultStatus.PRELIMINARY;
case ADDITIONAL:
return InvestigationResultStatus.ADDITIONAL;
case REJECTED:
return InvestigationResultStatus.REJECTED;
case UNDEFINED:
return InvestigationResultStatus.UNDEFINED;
default:
throw new IllegalArgumentException("Unknown InvestigationResultStatus: " + investigationResultStatus);
}
}
}
Now I want to test the InvestigationResultStatusMapper class default case path and throw IllegalArgumentException. I search and try it in many ways.But mosta of the examples are in using powermockito.but i want mackito with testNg.How can I test it.I try it using this,
#Test(expectedExceptions = { IllegalArgumentException.class })
public void testMapOmResultStatusToNovaResultStatus_whenHaveDefaultValue_thenThrowIllegalArgumentException()
{
se.api.InvestigationResultStatus investigationResultStatusNewEnum = mock(se.api.InvestigationResultStatus.class);
Whitebox.setInternalState(investigationResultStatusNewEnum, "name", "DEFAULT");
Whitebox.setInternalState(investigationResultStatusNewEnum, "ordinal", "DefaultResult.rr");
se.api.InvestigationResultStatus investigationResultStatusCosmic = mock(se.api.InvestigationResultStatus.class);
when(investigationResultStatusCosmic.values()).thenReturn(
new se.api.InvestigationResultStatus[] { InvestigationResultStatus.PENDING, InvestigationResultStatus.NEW,
se.api.InvestigationResultStatus.MODIFIED, InvestigationResultStatus.FINAL, InvestigationResultStatus.PRELIMINARY,
se.api.InvestigationResultStatus.ADDITIONAL, InvestigationResultStatus.REJECTED,
se.api.InvestigationResultStatus.COMPLETE, InvestigationResultStatus.UNDEFINED, investigationResultStatusNewEnum });
investigationResultStatusMapper.mapOmResultStatusToNovaResultStatus(investigationResultStatusNewEnum);
}
you can create directly an instance in order to avoid mocks.
#Test(expectedExceptions = { IllegalArgumentException.class })
public void testMapOmResultStatusToNovaResultStatus_whenHaveDefaultValue_thenThrowIllegalArgumentException()
{
InvestigationResultStatus investigation = new InvestigationResultStatus();
investigation.mapOmResultStatusToNovaResultStatus(-10000);
....
}
In this I'm unabale to mock enum(final) class using mockito
#BeforeClass
public void setUp()
{
investigationResultStatusMapper = new InvestigationResultStatusMapper();
investigationResultStatusNewEnum = PowerMockito.mock(InvestigationResultStatus.class);
Whitebox.setInternalState(investigationResultStatusNewEnum, "name", "TEST");
Whitebox.setInternalState(investigationResultStatusNewEnum, "ordinal", 9);
InvestigationResultStatus[] investigationResultStatuses = Arrays
.copyOf(InvestigationResultStatus.values(), InvestigationResultStatus.values().length + 1);
investigationResultStatuses[investigationResultStatuses.length - 1] = investigationResultStatusNewEnum;
PowerMockito.mockStatic(InvestigationResultStatus.class);
PowerMockito.when(InvestigationResultStatus.values()).thenReturn(
investigationResultStatuses);
}
/**
* Test Method for {#link InvestigationResultStatusMapper#mapOmResultStatusToNovaResultStatus(InvestigationResultStatus)}
* extend the InvestigationResultStatus enum for DEFAULT throw {#link IllegalArgumentException})
*/
#Test(expectedExceptions = {
IllegalArgumentException.class }, expectedExceptionsMessageRegExp = "Unknown InvestigationResultStatus: TEST")
public void testMapOmResultStatusToNovaResultStatus_whenHaveDefaultValue_thenThrowIllegalArgumentException()
{
investigationResultStatusMapper.mapOmResultStatusToNovaResultStatus(investigationResultStatusNewEnum);
}
Wrong approach. First of all, the 3rd party enum has 9 entries, but your own enum has only 8. So when your mapping code receives COMPLETE it will throw that exception! But if your own enum would have 9 entries, and you just map them 1-1 ... what is the point of using your own enum here in the first place? (there might be good reasons to have it, but at this point, it doesn't add much value).
Beyond that problem, you should do this completely different:
You put these mappings into a Map object. Then write a unit tests that uses InvestigationResultStatus.values() to acquire an array with all valid InvestigationResultStatus constants. You make sure that your map knows how to map all these valid constants (for example by checking that you get a non-null return value). Additionally, you might have one test for each 3rd party constant to check for the expected result.
The key point here is: unfortunately, enums might change over time. So you absolutely want to know when additional constants are added to the 3rd enum.
In other words: the java language respectively the compiler makes sure that there can't be "invalid" instances of that 3rd party enum. When you have InvestigationResultStatus someEnumValue, then it is 100% clear that: either someValue is null, or it is one of the constants from that enum. Nothing else is possible here! Thus: no need to expect, check or handle "invalid" values.

Java: replace switch with lambdas. Worth it?

Using blocks of code with switch or if is a common thing when checking for events. It can be clean code when made simple, but still seems to have more lines than needed, and could be simplified using lambdas.
Block with if:
if(action == ACTION_1){
doAction1();
} else if(action == ACTION_2){
doAction2();
} else {
doDefaultAction();
}
Block with switch:
switch(action){
case ACTION_1:
doAction1();
break;
case ACTION_2:
doAction2();
break;
default:
doDefaultAction();
}
Block with lambdas using the utility class With below:
with(action)
.when(ACTION_1, this::doAction1)
.when(ACTION_2, this::doAction2)
.byDefault(this::doDefaultAction)
Using lambdas has less code, but the question is: is it easier to read than the others? Easier to maintain? Regarding performance lambdas is the worst, but for cases where performance is not important the lambdas version is shorter than the switch/if blocks.
So, how do you see it? Maybe there is a Kotlin way shorter than this, I try to focus on java only, I love Kotlin but the compilation is still too slow for my projects.
A similar utility class could be used when the block must return a specific value.
FYI, the class for the lambdas is here, I didn't check for errors, just made it quickly for this example:
public class With<T> {
private final T id;
private boolean actionFound;
private With(T id) {
this.id = id;
}
public static <T> With<T> with(T id) {
return new With<>(id);
}
public With<T> when(T expectedId, Action action) {
if (!actionFound && id == expectedId) {
actionFound = true;
action.execute();
}
return this;
}
public void byDefault(Action action) {
if (!actionFound) {
action.execute();
}
}
#FunctionalInterface
interface Action {
void execute();
}
}
As a couple has said, replacing switch with compounded methods is less efficient. Depending on your use-case, it might even be worth it to use your implementation.
Funnily enough, Oracle is actually planning to implement lambdas within switch statements, as seen in this recent JEP.
Example:
String formatted = switch (s) {
case null -> "(null)";
case "" -> "(empty)";
default -> s;
}
The switch is more flexible in that you can call functions with varying numbers of arguments, or call more than one function. You can also more easily denote when two cases lead to the same action. The fact that it's faster is just a bonus.
So in that sense I'm not sure what your With class is really adding.
However, switch has a limited number of types that it can work with. Perhaps your With class would prove to be more useful if you were to pass it predicates rather than performing simple reference equality, for example:
public With<T> when(Predicate<T> expected, Action action) {
if (!actionFound && expected.test(id)) {
actionFound = true;
action.execute();
}
return this;
}
Sample usage:
final String test = "test";
with(test)
.when(String::isEmpty, this::doAction1)
.when(s -> s.length() == 3, this::doAction2)
.byDefault(this::doDefaultAction);
replace switch with lambdas. Worth it?
No.
Because in an OO language the replacemenst for a switch or an if/else cascade is polymorphism, not "fluent API".
One option to do this is to declare static final Map<T, Action> EXPECTED_ID_TO_ACTION. Then you just can EXPECTED_ID_TO_ACTION.getOrDefault(actionId, DEFAULT_ACTION).execute(), turning ugly switch or multiple ifs into one-liner.

What is the usage of default when the switch is for an enum?

Suppose I have an enum Color with 2 possible values: RED and BLUE:
public enum Color {
RED,
BLUE
}
Now suppose I have a switch statement for this enum where I have code for both 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 code block for both possible values of the enum, what is the usage of default in the above code?
Should I throw 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");
}
It is good practice to throw an Exception as you have shown in the second example. You improve the maintainability of your code by failing fast.
In this case it would mean if you later (perhaps years later) add an enum value and it reaches the switch statement you will immediately discover the error.
If the default value were not set, the code would perhaps run through even with the new enum value and could possibly have undesired behavior.
The other answers are correct in saying that you should implement a default branch that throws an exception, in case a new value gets added to your enum in the future. However, I would go one step further and question why you're even using a switch statement in the first place.
Unlike languages like C++ and C#, Java represents Enum values as actual objects, which means that you can leverage object-oriented programming. Let's say that the purpose of your method is to provide an RGB value for each color:
switch (color)
case RED:
return "#ff0000";
...
Well, arguably, if you want each color to have an RGB value, you should include that as part of its description:
public enum Color
{
RED("#FF0000"),
BLUE("#0000FF");
String rgb;
public Color(String rgb) {
this.rgb = rgb;
}
public getRgb() { return this.rgb; }
}
That way, if you add a new color later, you're pretty much forced to provide an RGB value. It's even more fail-fast than the other approach, because you'll fail at compile-time rather than run-time.
Note that you can do even more complicated things if you need to, including having each color provide its own custom implementation of an abstract method. Enums in Java are really powerful and object-oriented, and in most cases I've found I can avoid needing to switch on them in the first place.
Compile time completeness of the switch cases doesn't guarantee runtime completenes.
Class with a switch statement compiled against an older version of enum may be executed with a newer enum version (with more values). That's a common case with library dependencies.
For reasons like these, the compiler considers the switch without default case incomplete.
In small programs, there is no practical use for that, but think of a complex system that speards among large number of files and developers - if you define the enum in one file and use it in another one, and later on someone adds a value to the enum without updating the switch statement, you'll find it very useful...
If you've covered all of the possibilities with your various cases and the default cannot happen, this is the classic use case for assertions:
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");
}
To satisfy IDEs and other static linters, I often leave the default case in as a no-op, along with a comment such as // Can't happen or // Unreachable
i.e., if the switch is doing the typical thing of handling all possible enum values, either explicitly or via fall-throughs, then the default case is probably programmer error.
Depending on the application, I sometimes put an assertion in the case to guard against programmer error during development. But this has limited value in shipping code (unless you ship with assertions enabled.)
Again, depending on the situation I might be convinced to throw an Error, as this is really an unrecoverable situation -- nothing the user can do will correct what is probably programmer error.
Yes, you should do it. You may change enum but don't change switch. In the future it'll lead to mistakes. I think that throw new IllegalArgumentException(msg) is the good practice.
When the enum constants are too many and you need to handle only for few cases, then the default will handle the rest of the constants.
Also, enum constants are references, if the reference is not yet set, or null. You may have to handle such cases too.
Yes, it is dead code until someone add a value to the enum, which will make your switch statement follow the principle of 'fail fast' (https://en.wikipedia.org/wiki/Fail-fast)
This could relates to this question : How to ensure completeness in an enum switch at compile time?
Apart from the possible future extending of the enum, which was pointed out by many, some day someone may 'improve' yout getColor() or override it in a derived class and let it return an invalid value. Of course a compiler should catch that, unless someone explicitly forces unsafe type casting...
But bad things just happen, and it's a good practice not to leave any unexpected else or default path unguarded.
I'm surprised nobody else mentioned this. You can cast an int to an enum and it won't throw just because the value is not one of the enumerated values. This means (among other things), the compiler cannot tell that all the enum values are in the switch.
Even if you write your code correctly, this really does come up when serializing objects that contain enums. A future version might add to the enum and your code choke on reading it back, or somebody looking to create mayhem may hexedit a new value in. Either way, running off the switch rarely does the right thing. So, we throw in default unless we know better.
Here is how I would handle it, beside NULL value which would result in a null pointer exception which you can handle.
If Color color is not null, it has to be one of the singletons in enum Color, if you assign any reference to an object that is not one of the them this will cause a Runtime error.
So my solution is to account for 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; }
}
In this case using Assertion in default is the best practice.

Is returning null from a "get error message" method an anti-pattern? [closed]

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 9 years ago.
Is it "bad practice" to effectively cache the result of executing expensive stateless checking code by returning a null in the case of a "no problem found"? The upside is minimal code and no class/code bloat.
This is illustrated by this code:
public static String getErrorMessage(SomeState x) {
// do some "expensive" processing with "x"
if (someProblem)
return "Some problem";
if (someOtherProblem)
return "Some other problem";
return null; // no error message means "all OK"
}
And the calling code:
String message = getErrorMessage(something);
if (message != null) {
display(message);
return;
}
// proceed
This pattern avoids having to repeat executing the expensive code twice by returning null to mean "there's no error message, because there's no error". And there's no extra "low value" classes/code.
The obvious alternatives are A) to separate out the concerns of checking and message creation:
public static boolean isOK(SomeState x) {
// do some "expensive" processing with "x"
return thereIsNoProblem;
}
public static String getErrorMessage(SomeState x) {
// do some "expensive" processing with "x"
if (someProblem)
return "Some problem";
if (someOtherProblem)
return "Some other problem";
}
And the calling code:
if (!isOK(something)) {
display(getErrorMessage(something)); // expensive code called a second time here
return;
}
// proceed
which executes the expensive code once to determine if there's a problem, and again to determine what the problem is, or B) to return a "result" object that has a boolean field to answer the "if" part and a String field to answer the "message" part, eg
class MyResult { // like a struct in C
boolean ok;
String message;
// accessor methods omitted
}
public static MyResult verify(SomeState x) { ...}
And the calling code:
MyResult result = verify(something);
if (!result.ok) { // spare me the lecture on accessors - this is illustrative only
display(result.message);
return;
}
which creates class bloat and is a little clumsy IMHO.
Is it "bad" to "overload" the return value in this way?
It is certainly "neater" than all the alternatives I can think of.
If you offer an alternative, say why you think returning null is "bad". State the risks or downside to justify not using this simple technique.
There are a limited number of options for reporting an error/abnormal condition within a called function:
Return a value (if the function can/does return a value) that indicates "error".
Return an error code at a location indicated by a "error pointer" parameter to the function.
Throw an exception.
Pass control to a previously-defined (or parameter-designated) "delegate" or "callback".
Update a global error indicator.
Invoke a global error routine.
None of these is especially attractive -- global is bad, we know, a delegate/callback is clumsy and verbose, and exceptions are slow (and the mark of the beast, as we all know). So the first two are the options most often used.
With 2 there is the problem that if you do return an error from a value-returning function, you still need to return a value. And, for an object-returning function, nil is the most logical value to return.
You kind of end up returning nil regardless.
I would use:
public class ValidationResult {
public final boolean isValid;
public final String errorMessage; // may be null if isValid == true
public ValidationResult(boolean isValid, String errorMessage) {
if (!isValid && errorMessage == null) {
throw new IllegalArgumentException();
}
this.isValid = isValid;
this.errorMessage = errorMessage;
}
}
Then:
public static ValidationResult validate(SomeState x) {
// blah
return new ValidationResult(isValidTest, errorMessageIfNot);
}
And:
ValidationResult r = validate();
if (!r.isValid) {
display(r.errorMessage);
}
Alternatively, you could have it throw a checked exception.
public class ValidationException extends Exception {
// your code here
}
Then:
public static boolean validate(SomeState x) throws ValidationException {
// ...
if (isValid) {
return true;
} else {
throw new ValidationException(message):
}
}
Your initial solution is perfectly fine, null is perfect to represent the "absence of" a return value.
I've decided to work on my comment to make it an answer. I think this is a good context for an enumeration, something like this:
public enum OperationStatus {
ERROR1 {
#Override
public String toString() {
return "err1";
}
},
ERROR2
//The same as error 1
//
//Many error types here
SUCCESS {
#Override
public String toString() {
return "ok";
}
}
}
This way, your method can return a value of this enumeration specifying the actual error with a code that you can get as a String. This is in particular if you plan to localize such errors by having, for example, localized bundles that you can switch and obtain the associated value to each of the defined codes.
On the other hand, this would work if you don't plan to add errors in the future or if you can afford the occasional modifications this enumeration would require if new error types arise.
An alternative (you'd still need to update this array, but you prevent yourself from having an enumeration) to avoid the enum, a simple Stirng[] would do the trick too, since you can return an int as an error code and use it as an index of that array to obtain the actual code you can interpret, display and localize.
Instead of checking isOK() then getting the error message, just add a throws exception to isOK() with appropriate message, then use a try/catch to display the error message.
There doesn't seem to be a reason to have multiple layers of checking then getting the errors when one function would suffice with throws.
It's ok as long as you are using it by yourself but it could be improved somehow. About why it could be improved, let me quote Tony Hoare:
I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.
First of all you could use exceptions:
public static String getErrorMessage(SomeState x) throws YourException {
// do some "expensive" processing with "x"
if (someProblem)
throw YourException("Some problem");
if (someOtherProblem)
throw YourException("Some other problem");
return null; // no error message means "all OK"
}
Then you can have a custom object:
class ErrorState {
boolean hasFoundError;
String message;
ErrorState() {
hasFoundError = false;
}
ErrorState(String message) {
hasFoundError = true;
this.message = message;
}
public final static ErrorState NO_ERROR = new ErrorState();
}
Finally if the set of potential errors is finite you could just use an enum (which is in my opinion the better choice):
enum ErrorState {
NO_ERROR(""),
SOME_ERROR("Some error"),
SOME_OTHER_ERROR("Some other error");
public final String message;
ErrorState(String message) { this.message = message; }
}
I'm pretty anti-NULL, and, personally, would return the empty String "" to mean no error, or perhaps a constant like "OK". But null is acceptable.
The enum / value object concept does feel a bit like overkill, but often the requirements expand and you'll eventually need something like that.
In cases where there can be multiple errors, I like to return a List of Strings, and an empty list obviously means no problemo. In this case, do not return null, return an empty list.

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.
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.
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.
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;
}
}
}
I'd say it's probably because model.GetMode() could return null.
A nice way for this would be to add the default case to return some error value or throw exception and to use automated tests with jUnit, for example:
#Test
public void testEnum() {
for(Mode m : Mode.values() {
m.foobar(); // The switch is separated to a method
// If you want to check the return value, do it (or if there's an exception in the
// default part, that's enough)
}
}
When you got automated tests, this will take care of that foobar is defined for all enumerations.
Create a default case that throws an exception:
throw new RuntimeExeption("this code should never be hit unless someone updated the enum")
... and that pretty much describes why Eclipse is complaining: while your switch may cover all enum cases today, someone could add a case and not recompile tomorrow.
Why does EclipseBuilder 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?
It's not an issue in Eclipse, but rather the compiler, javac. All javac sees is that you don't have a return value in the case in which nothing is matched (the fact that you know you are matching all cases is irrelevant). You have to return something in the default case (or throw an exception).
Personally, I'd just throw some sort of exception.
Your problem is that you are trying to use the switch statement as an indicator that your enum is locked down.
The fact is that the 'switch' statement and the java compiler cannot recognize that you do not want to allow other options in your enum. The fact that you only want three options in your enum is completely separate from your design of the switch statement, which as noted by others should ALWAYS have a default statement. (In your case it should throw an exception, because it is an unhandled scenario.)
You should liberally sprinkle your enum with comments so that everyone knows not to touch it, and you should fix your switch statement to throw errors for unrecognized cases.
That way you've covered all the bases.
EDIT
On the matter of throwing compiler error. That does not strictly make sense. You have an enum with three options, and a switch with three options. You want it to throw a compiler error if someone adds a value to the enum. Except that enums can be of any size, so it doesn't make sense to throw a compiler error if someone changes it. Furthermore, you are defining the size of your enum based on a switch statement which could be located in a completely different class.
The internal workings of Enum and Switch are completely separate and should remain uncoupled.
Since I can't just comment...
Always, Always, Always have a default case. You'd be surprised how "frequently" it would be hit (Less in Java than C, but still).
Having said that, what if I only want to handle only on/off in my case. Your semantic processing by javac would flag that as an issue.
Nowadays (this answer is written several years after the original question), eclipse allows following configuration at Window -> Preferences -> Java -> Compiler -> Error/warnings -> Potential programming problems:
Incomplete switch cases
Signal even if default case exists

Categories

Resources