I have a statement that goes like this:
public $type function_name($enum e){
if(e != null)
switch(e){
case A: return x1;
case B: return x2;
case C: return x3;
//default: do some operations
}
// default or e = null
do the exact same operations
}
My question is: given that the default case is treated in the rest of the method, is it necessary (in terms of good coding style) to add it to the switch statement (basically, duplicate the code for "if anything else happens" - in this case, default for switch, e = null, etc.)?
Default case is not necessary in Switch-Case, but it is a good habit to use it and define a default case if non of the cases you have defined occur. If you're duplicating your code, it is not good!
Hope it helps.
default case is not necessary but you should always have it and log/throw exceotion. In case of some error or problem with the value passed you will know right away the problem.
If you add it to the default clause, the last line
throw new YException();
will be dead code.
Note that in no case, the code will reach the last line. If e meets the cases A,B or C, it will reach a return statement, so it will exit the function. And if none of this cases are met, the throw in the default will be reached.
In conclusion: you should add it to the default clause (it's a good practise to always include a default) and delete the last throw line.
if in combination with switch for the same variable does not make sense to me.
It can work, but it is asking for trouble.
Why not put the exception as a default action? Than you do not need duplicated code at all.
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();
This is the code I have:
public enum Modification {
NONE, SET, REMOVE;
}
boolean foo(){
for (S s : sList) {
final Modification modification = s.getModification();
switch (modification) {
case SET:
case REMOVE:
return true;
/*
case NONE:
break;
*/
}
}
return false;
}
And when the code is as seen above, IntelliJ will say:
'for' statement does not loop less... () Reports any instance of for,
while and do statements whose bodies are guaranteed to execute at most
once. Normally, this is an indication of a bug.
Only if I make the following change, IntelliJ will be happy:
for (S s : sList) {
final Modification modification = s.getModification();
switch (modification) {
case SET:
case REMOVE:
return true;
case NONE:
break;
}
}
Why is my for loop not looping if case NONE: is not included in the switch statement?
I just tried this in eclipse and you end up with a compiler warning on the switch statement.
The enum constant NONE needs a corresponding case label in this enum switch on Modification
To resolve the warning I'm given the following options.
Add default case
Add missing case statements
Add #SuppressWarnings 'incomplete-switch' to foo()
If I add the missing case statement then the warning no longer appears. The same as adding the missing case makes your error warning disappear from intellij.
Without the statement for case NONE you can only see two cases, both of which return true. Without knowing the structure of Modification and the extra value of NONE it looks like this loop would just return true on the first iteration of the loop.
Of course the compiler should actually know that there are more values for Modification than SET and REMOVE so the warning is just for good style. Basically your code works but here's how to improve it.
I would choose to add a default statement rather than the missing case. This would be more future proof in case more values are later added to the enum. E.G.
switch (modification)
{
case SET:
case REMOVE:
return true;
default:
break;
}
Personally I'm not a fan of using the fall through on switch statements. What you gain in making the code concise you lose in legibility IMHO. If someone later comes and adds a case between SET and REMOVE it could introduce a bug. Also, having a return statement mid-way through a method can also cause problems. If someone wants to add some code just before the return they may miss all the places. If the method is very simple then multiple returns is fine but you've stated that this is a simplified example and so if this block of code is complicated I would avoid it.
If you're able to use Java 8 then this looks to be the perfect use case for the new stream API. Something like the following should work.
return sList.stream().anyMatch(
modification -> (modification==Modification.SET || modification==Modification.REMOVE)
);
i assume these are your only three cases right?, so basically its saying you are going to hit one of the first two and instantly return true, therefore not looping, just add a default case and everything should work ok, this is good practice also btw.
basically it cant see a case where it doesnt just return instantly without iterating the loop
I'd say its a false positive.
1st indication:
If you run your code through a debugger - and have elements with NONE modification in the list before an element with other modifications - it will actually loop.
2nd indication:
When you look at the generated bytecode, it transforms the switch statement to (sort of - its not exactly the same)
for (S s : sList) {
Modification modification = s.getModification();
switch (modification.ordinal()) {
case 1:
case 2:
return true;
}
}
If you put that in your code, IntelliJ does not complain.
3rd indication:
the warning dissappears if you put an additional statement before the return, i.e. System.out.println();
switch (modification) {
case SET:
case REMOVE:
System.out.println()
return true;
Seems you tricked the inspection with the missing case label and could simply ignore the warning.
I think that IntelliJ's inspections is wrong. I reported it to JetBrains
Edit : it's fixed
Your switch case always breaks or returns. In the first case, you do nothing aka it falls through. The second case returns which causes both the switch and the loop to stop. In the third case you break the switch statement which causes it to stop. It does not however stop the for loop (aka, it keeps iterating).
Either add specific functionality for the SET case or change your behaviour on the REMOVE and NONE cases.
public enum Modification {
NONE, SET, REMOVE;
}
boolean foo(){
for (S s : sList) {
final Modification modification = s.getModification();
switch (modification) {
case SET:
// This case falls through to the REMOVE case
case REMOVE:
return true; // This statement stops the switch, loop and returns true
case NONE:
break; // This statement stops the switch and continues the loop.
}
}
return false;
}
Your switch is not looping without the NONE case because return breaks the loop and returns a value from the function. break breaks the switch loop but continues the for loop.
By request of OP an extra explanation.
Falling through means the next case will be executed until a stop (break or return) is reached. This makes the following code snippets equivelant:
case SET:
case REMOVE:
return true;
is the same as:
case SET:
return true;
case REMOVE:
return true;
I have never understood what is assert used for, even though I have read plenty examples, they don't really let me know what or why should I use it for.
So Instead of asking an example, I'm gonna provide one and let me know if this is the proper usage of assert.
// The idea is that the `mode` variable should be 0 or 1, and no other number.
switch(mode) {
case 0:
// do stuff
break;
case 1:
// do other stuff
break;
default:
// assert code?
}
If this is correct, please let me know how to use it in this case. If this is not how it is supposed to use, please provide an example.
Not in this case.
If you're asserting a value, you're making a statement that, before some critical evaluation is done using this value, that it is what you assert it to be. You can assert that the value isn't null, or that it's less than 2, or something before you reach your critical code block.
assert (mode >= 0 && mode < 2); // Ensures that `mode` is between 0 and 1.
// Switch statement to follow
I would not encourage the use of that here. Your code would not read well, and unless you enable assertions with the -ea flag, your assertion would not work.
Instead, what you can do is throw an exception of some kind - if it's not 0 or 1, then the mode is an illegal value which cannot be processed, leading to exceptional/undefined behavior. Throw an exception of some kind.
switch(mode) {
case 0:
// do stuff
break;
case 1:
// do other stuff
break;
default:
throw new IllegalArgumentException("Mode is illegal");
}
assert object != null;
object.doSomething();
assert is used to verify the correctness of some precondition, invariant, or postcondition. In the example, we want to make sure object is not null when some method is called on it.
One thing to remember is that assert should never be executed in production code. We only make use of it when testing. There is a Java option to turn it on or off.
As for your specific example, you could use:
assert mode == 0;
assert mode == 1;
at the very beginning of the switch block to make sure only 0 and 1 are passed in.
P.S. The discussion on when to use assertion vs exception might help your understanding. The idea is that
Exceptions address the robustness of your application while assertions
address the correctness of your application.
Assertions are basically used to check something that should never happen.
Some assertions use cases from http://www.javapractices.com/topic/TopicAction.do?Id=102
pre-conditions (in private methods only) - the requirements which a method requires its caller to fulfill.
post-conditions - verify the promises made by a method to its caller
class invariants - validate object state
unreachable-at-runtime code - parts of your program which you expect to be unreachable, but which cannot be verified as such at compile-time (often else clauses and default cases in switch statements)
So the usage of assertion in your code is not correct
I have found myself using the following practice, but something inside me kind of cringes every time i use it. Basically, it's a precondition test on the parameters to determine if the actual work should be done.
public static void doSomething(List<String> things)
{
if(things == null || things.size() <= 0)
return;
//...snip... do actual work
}
It is good practice to return at the earliest opportunity.
That way the least amount of code gets executed and evaluated.
Code that does not run cannot be in error.
Furthermore it makes the function easier to read, because you do not have to deal with all the cases that do not apply anymore.
Compare the following code
private Date someMethod(Boolean test) {
Date result;
if (null == test) {
result = null
} else {
result = test ? something : other;
}
return result;
}
vs
private Date someMethod(Boolean test) {
if (null == test) {
return null
}
return test ? something : other;
}
The second one is shorter, does not need an else and does not need the temp variable.
Note that in Java the return statement exits the function right away; in other languages (e.g. Pascal) the almost equivalent code result:= something; does not return.
Because of this fact it is customary to return at many points in Java methods.
Calling this bad practice is ignoring the fact that that particular train has long since left the station in Java.
If you are going to exit a function at many points in a function anyway, it's best to exit at the earliest opportunity
It's a matter of style and personal preference. There's nothing wrong with it.
To the best of my understanding - no.
For the sake of easier debugging there should be only one return/exit point in a subroutine, method or function.
With such approach your program may become longer and less readable, but while debugging you can put a break point at the exit and always see the state of what you return. For example you can log the state of all local variables - it may be really helpful for troubleshooting.
It looks like there a two "schools" - one says "return as early as possible", whereas another one says "there should be only one return/exit point in a program".
I am a proponent of the first one, though in practice sometimes follow the second one, just to save time.
Also, do not forget about exceptions. Very often the fact that you have to return from a method early means that you are in an exceptional situation. In your example I think throwing an exception is more appropriate.
PMD seems to think so, and that you should always let your methods run to the end, however, for certain quick sanity checks, I still use premature return statements.
It does impair the readability of the method a little, but in some cases that can be better than adding yet another if statement or other means by which to run the method to the end for all cases.
There's nothing inherently wrong with it, but if it makes you cringe, you could throw an IllegalArgumentException instead. In some cases, that's more accurate. It could, however, result in a bunch of code that look this whenever you call doSomething:
try {
doSomething(myList);
} catch (IllegalArgumentException e) {}
There is no correct answer to this question, it is a matter of taste.
In the specific example above there may be better ways of enforcing a pre-condition, but I view the general pattern of multiple early returns as akin to guards in functional programming.
I personally have no issue with this style - I think it can result in cleaner code. Trying contort everything to have a single exit point can increase verbosity and reduce readability.
It's good practice. So continue with your good work.
There is nothing wrong with it. Personally, I would use else statement to execute the rest of the function, and let it return naturally.
If you want to avoid the "return" in your method : maybe you could use a subClass of Exception of your own and handle it in your method's call ?
For example :
public static void doSomething(List<String> things) throws MyExceptionIfThingsIsEmpty {
if(things == null || things.size() <= 0)
throw new MyExceptionIfThingsIsEmpty(1, "Error, the list is empty !");
//...snip... do actual work
}
Edit :
If you don't want to use the "return" statement, you could do the opposite in the if() :
if(things != null && things.size() > 0)
// do your things
If function is long (say, 20 lines or more), then, it is good to return for few error conditions in the beginning so that reader of code can focus on logic when reading rest of the function. If function is small (say 5 lines or less), then return statements in the beginning can be distracting for reader.
So, decision should be based on primarily on whether the function becomes more readable or less readable.
Java good practices say that, as often as possible, return statements should be unique and written at the end of the method. To control what you return, use a variable. However, for returning from a void method, like the example you use, what I'd do would be perform the check in a middle method used only for such purpose. Anyway, don't take this too serious - keywords like continue should never be used according to Java good practices, but they're there, inside your scope.
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();