I am implementing a simple switch case which will switch on an Enum value. Following is the code
ScheduleType scheduleType = ScheduleType.valueOf(scheduleTypeString);
switch (scheduleType) {
case ScheduleType.CRON_EXPRESSION:
System.out.println("Cron");
break;
}
But I am getting the following error in my IDE:
The qualified case label ScheduleType.CRON_EXPRESSION must be replaced with the unqualified enum constant CRON_EXPRESSION
Can somebody explain why do I get this error and what's wrong with the code.
I know the right way to do is to remove the ClassName, but why do I need to do that?
Because generally in comparisons I do use it like for example in equals and all.
Thanks
Leave off the class name. case ScheduleType.CRON_EXPRESSION: should be case CRON_EXPRESSION: instead.
Or in other words, ScheduleType.CRON_EXPRESSION must be replaced with the unqualified enum constant CRON_EXPRESSION.
Mike has explained the how part.
I shall attempt to explain the why part.
The switch case would make sense only if you compare enums of the same type.
Comparing enums of type E1 with that of E2 doesn't make sense.
There was a bug which was raised to request for this feature which touches upon this.
Related
Normally, default is not necessary in a switch statement. However, in the following situation the code successfully compiles only when I uncomment the default statement. Can anybody explain why?
public enum XYZ {A,B};
public static String testSwitch(XYZ xyz)
{
switch(xyz)
{
case A:
return "A";
case B:
//default:
return "B";
}
}
The reason that you have to uncomment the default is that your function says that it returns a String, but if you only have case labels defined for A and B then the function will not return a value if you pass in anything else. Java requires that all functions that state that they return a value actually return a value on all possible control paths, and in your case the compiler isn't convinced that all possible inputs have a value returned.
I believe (and I'm not sure of this) that the reason for this is that even if you cover all your enum cases, the code could still fail in some cases. In particular, suppose that you compile the Java code containing this switch statement (which works just fine), then later on change the enum so that there's now a third constant - let's say C - but you don't recompile the code with the switch statement in it. Now, if you try writing Java code that uses the previously-compiled class and passes in C into this statement, then the code won't have a value to return, violating the Java contract that all functions always return values.
More technically speaking, I think the real reason is that the JVM bytecode verifier always rejects functions in which there is some control path that falls off the end of a function (see §4.9.2 of the JVM spec), and so if the code were to compile it would just get rejected by the JVM at runtime anyway. The compiler therefore gives you the error to report that a problem exists.
I think this is explained by the JLS definite assignment rules for switch statements (JLS 16.2.9) which states the following:
"V is [un]assigned after a switch statement iff all of the following are true:
Either there is a default label in the switch block or V is [un]assigned after the switch expression.
If we then apply this to the notional V which is the return value of the method, we can see that if there is no default branch, the value would be notionally unassigned.
OK ... I'm extrapolating definite assignment rules to cover return values, and maybe they don't. But the fact that I couldn't find something more direct in the spec doesn't mean it isn't there :-)
There's another (more sound) reason why the compiler has to give an error. It stems from the binary compatibility rules for enum (JLS 13.4.26) which state the following:
"Adding or reordering constants from an enum type will not break compatibility with pre-existing binaries."
So how does that apply in this case? Well suppose that the compiler was allowed to infer that the OP's example switch statement always returned something. What happens if the programmer now changes the enum to add an extra constant? According to the JLS binary compatibility rules, we haven't broken binary compatibility. Yet the method containing the switch statement can now (depending on its argument) return an undefined value. That cannot be allowed to happen, so therefore the switch must be a compilation error.
In Java 12 they have introduced enhancements to switch that include switch expressions. This runs into the same problem with enums that change between compile time and runtime. According to the JEP 354, they solving this problem as follows:
The cases of a switch expression must be exhaustive; for all possible values there must be a matching switch label. (Obviously switch statements are not required to be exhaustive.)
In practice this normally means that a default clause is required; however, in the case of an enum switch expression that covers all known constants, a default clause is inserted by the compiler to indicate that the enum definition has changed between compile-time and runtime. Relying on this implicit default clause insertion makes for more robust code; now when code is recompiled, the compiler checks that all cases are explicitly handled. Had the developer inserted an explicit default clause (as is the case today) a possible error will have been hidden.
The only thing that is not crystal clear is what the implicit default clause would actually do. My guess is that it would throw an unchecked exception. (As of right now, the JLS for Java 12 has not been updated to describe the new switch expressions.)
In Java 12 you can use the preview switch expression feature (JEP-325) as follows:
public static String testSwitch(XYZ xyz) {
return switch (xyz) {
case A -> "A";
case B -> "B";
};
}
and you don't need default case as long as you handle all enum values in switch.
Note, to use a preview feature you'll have to pass --enable-preview --source 12 options to javac and java
As has been stated, you need to return a value and the compiler doesn't assume that the enum cannot change in the future. E.g. you can create another version of the enum and use that without recompiling the method.
Note: there is a third value for xyz which is null.
public static String testSwitch(XYZ xyz) {
if(xyz == null) return "null";
switch(xyz){
case A:
return "A";
case B:
return "B";
}
return xyz.getName();
}
This ha the same result as
public static String testSwitch(XYZ xyz) {
return "" + xyz;
}
The only way to avoid a return is to throw an exception.
public static String testSwitch(XYZ xyz) {
switch(xyz){
case A:
return "A";
case B:
return "B";
}
throw new AssertionError("Unknown XYZ "+xyz);
}
There is a contract that this method has to return a String unless it throws an Exception. And everytime is not limited to those cases where the value of xyz is equal to XVZ.A or XYZ.B.
Here's another example, where it's obviuos, that the code will run correct but where we have a compiletime error for the very same reason:
public boolean getTrue() {
if (1 == 1) return true;
}
It is not true that you have to add a default statement, it is true, that you have to return a value at any time. So either add a default statement or add a return statement after the switch block.
Because compiler cannot guess that there are only two values in the enum and forces you to return value from the method. (However I dont know why it cannot guess, maybe it has something with reflection).
what happens when xyz is null in your code example? In that case the method is missing a return statement.
default: throw new AssertionError();
The rule squid:128 seems to exist to prevent fall-through in switch case unless explicitly stated. It seems a reasonable rule, as forgetting a break is a common mistake.
However fall-through are perfectly valid when wanted.
The Documentation of this rule states that the only way to achieve a fall-through is to use continue
case 4: // Use of continue statement
continue;
I have also checked the source code of SwitchCaseWithoutBreakCheck are the implementation really check for "continue" statement
#Override
public void visitContinueStatement(ContinueStatementTree tree) {
super.visitContinueStatement(tree);
markSwitchCasesAsCompliant();
}
However, the Java language does not support continue in switch/case. Nor the online documentation nor ./java-checks/src/test/files/checks/SwitchCaseWithoutBreakCheck.java are valid Java programs.
Am I missing something or is this rule fully broken and prevent using fall-through ?
You are totally right in saying that the description here is wrong and then you actually have no way to do not trigger the rule if you want to actually use fallthrough (and thus you might want either to mark issue as false positive in this case or deactivate the rule alltogether)
calling the rule "broken" is an opinion so I won't argue on that ;)
Nevertheless, a ticket has been created to handle the issue : http://jira.sonarsource.com/browse/SONARJAVA-1169
I have a problem concerning switch/case statements in java in combination with enums.
In my code I want to do something based on the Enum of the type "MatchingMethods" set in the object "currentMethod".
The enum "MatchingMethods" contains several enums in the form
{EXACT_STRING_MATCHING, DEPTH_MATCHING, [...]}
Now the strange thing is though the object "currentMethod" contains an enum of the type "EXACT_STRING_MATCHING" not only the first case is executed but also the second one.
I know there is no break statement after the code of the first case but the code of the second case shouldn't be executed in my opinion because the enum "EXACT_STRING_MATCHING" doesn'T match with "DEPTH_MATCHING".
If I put in a break statement after the first case it seem to be totally fine…
My code is the following:
[...]
MatchingMethods mM = currentMethod.getMatchMethod();
switch (currentMethod.getMatchMethod()) {
case EXACT_STRING_MATCHING:
//do something here
case DEPTH_MATCHING:
comparedNodePair.setDepthMatchResult(currentMetricResult);
break;
[...]
I am totally confused…
May someone be able to help me?
You already mentioned it, you have no break - switch works like goto where the case are labels to be jumped at and no "boundaries" or functions.
This is also the biggest critique concerning switch, because no one would use goto today, but switch which is certainly similar.
But it gets executed, because once one of the case satements is true the flow of execution "falls trough" see here for some information
this is normal if there is no break statement at the end of the case block.
add the break statement is necessary if you only want the exact block to be executed.
To me, the following Java code is perfectly valid, good style:
enum Side { LEFT, RIGHT };
...
Side side = ...;
switch (side) {
case LEFT:
// do something
break;
case RIGHT:
// do something
break;
}
For SonarQube’s rule SwitchLastCaseIsDefaultCheck, this is not good enough, it wants a default case. Now here, a default case is superfluous, since the enumeration is covered completely.
For enumerations, I would like to see a test that checks whether the enumeration is completely covered and complain if it is not covered and has no default case (Eclipse can do that). Either should be fine. In fact, completely covering an enumeration allows for a compile-time warning later when the enumeration is extended, while giving a default case will fail only at run-time.
Optionally, both completely covering the enumeration and giving a default case could trigger a warning for unreachable code.
I would suggest it is good practice to always include a default case which throws an appropriate RuntimeException. This way, you guard against a future developer adding something to the enum and forgetting to update the switch statement.
Eclipse is an IDE, it help you to develop the programs in a selected language here in your case it is JAVA, it doesn't do any thing which is not specified in language, so it is not the problem of eclipse, it is all about the specification of language.
As it turns out, three years after asking the question I find that squid:SwitchLastCaseIsDefaultCheck now checks for complete coverage of an enum. Probably has for some time now, at least for 4.4.0.8066 of the Java plugin from Sonarqube I can confirm that. And that’s a very satisfactory answer for me.
Normally, default is not necessary in a switch statement. However, in the following situation the code successfully compiles only when I uncomment the default statement. Can anybody explain why?
public enum XYZ {A,B};
public static String testSwitch(XYZ xyz)
{
switch(xyz)
{
case A:
return "A";
case B:
//default:
return "B";
}
}
The reason that you have to uncomment the default is that your function says that it returns a String, but if you only have case labels defined for A and B then the function will not return a value if you pass in anything else. Java requires that all functions that state that they return a value actually return a value on all possible control paths, and in your case the compiler isn't convinced that all possible inputs have a value returned.
I believe (and I'm not sure of this) that the reason for this is that even if you cover all your enum cases, the code could still fail in some cases. In particular, suppose that you compile the Java code containing this switch statement (which works just fine), then later on change the enum so that there's now a third constant - let's say C - but you don't recompile the code with the switch statement in it. Now, if you try writing Java code that uses the previously-compiled class and passes in C into this statement, then the code won't have a value to return, violating the Java contract that all functions always return values.
More technically speaking, I think the real reason is that the JVM bytecode verifier always rejects functions in which there is some control path that falls off the end of a function (see §4.9.2 of the JVM spec), and so if the code were to compile it would just get rejected by the JVM at runtime anyway. The compiler therefore gives you the error to report that a problem exists.
I think this is explained by the JLS definite assignment rules for switch statements (JLS 16.2.9) which states the following:
"V is [un]assigned after a switch statement iff all of the following are true:
Either there is a default label in the switch block or V is [un]assigned after the switch expression.
If we then apply this to the notional V which is the return value of the method, we can see that if there is no default branch, the value would be notionally unassigned.
OK ... I'm extrapolating definite assignment rules to cover return values, and maybe they don't. But the fact that I couldn't find something more direct in the spec doesn't mean it isn't there :-)
There's another (more sound) reason why the compiler has to give an error. It stems from the binary compatibility rules for enum (JLS 13.4.26) which state the following:
"Adding or reordering constants from an enum type will not break compatibility with pre-existing binaries."
So how does that apply in this case? Well suppose that the compiler was allowed to infer that the OP's example switch statement always returned something. What happens if the programmer now changes the enum to add an extra constant? According to the JLS binary compatibility rules, we haven't broken binary compatibility. Yet the method containing the switch statement can now (depending on its argument) return an undefined value. That cannot be allowed to happen, so therefore the switch must be a compilation error.
In Java 12 they have introduced enhancements to switch that include switch expressions. This runs into the same problem with enums that change between compile time and runtime. According to the JEP 354, they solving this problem as follows:
The cases of a switch expression must be exhaustive; for all possible values there must be a matching switch label. (Obviously switch statements are not required to be exhaustive.)
In practice this normally means that a default clause is required; however, in the case of an enum switch expression that covers all known constants, a default clause is inserted by the compiler to indicate that the enum definition has changed between compile-time and runtime. Relying on this implicit default clause insertion makes for more robust code; now when code is recompiled, the compiler checks that all cases are explicitly handled. Had the developer inserted an explicit default clause (as is the case today) a possible error will have been hidden.
The only thing that is not crystal clear is what the implicit default clause would actually do. My guess is that it would throw an unchecked exception. (As of right now, the JLS for Java 12 has not been updated to describe the new switch expressions.)
In Java 12 you can use the preview switch expression feature (JEP-325) as follows:
public static String testSwitch(XYZ xyz) {
return switch (xyz) {
case A -> "A";
case B -> "B";
};
}
and you don't need default case as long as you handle all enum values in switch.
Note, to use a preview feature you'll have to pass --enable-preview --source 12 options to javac and java
As has been stated, you need to return a value and the compiler doesn't assume that the enum cannot change in the future. E.g. you can create another version of the enum and use that without recompiling the method.
Note: there is a third value for xyz which is null.
public static String testSwitch(XYZ xyz) {
if(xyz == null) return "null";
switch(xyz){
case A:
return "A";
case B:
return "B";
}
return xyz.getName();
}
This ha the same result as
public static String testSwitch(XYZ xyz) {
return "" + xyz;
}
The only way to avoid a return is to throw an exception.
public static String testSwitch(XYZ xyz) {
switch(xyz){
case A:
return "A";
case B:
return "B";
}
throw new AssertionError("Unknown XYZ "+xyz);
}
There is a contract that this method has to return a String unless it throws an Exception. And everytime is not limited to those cases where the value of xyz is equal to XVZ.A or XYZ.B.
Here's another example, where it's obviuos, that the code will run correct but where we have a compiletime error for the very same reason:
public boolean getTrue() {
if (1 == 1) return true;
}
It is not true that you have to add a default statement, it is true, that you have to return a value at any time. So either add a default statement or add a return statement after the switch block.
Because compiler cannot guess that there are only two values in the enum and forces you to return value from the method. (However I dont know why it cannot guess, maybe it has something with reflection).
what happens when xyz is null in your code example? In that case the method is missing a return statement.
default: throw new AssertionError();