Java: replace switch with lambdas. Worth it? - java

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.

Related

Java Error/Exception handling with returning value

So my friend and I are programming Blackjack in Java, and we wanted to test our input fields for the correct input(e.g only number input). So we sat at his PC and he wrote this solution:
public static boolean testeTextFieldInt(JTextField textField, int geld) {
if (!textField.getText().isEmpty()) {
try {
if(Integer.parseInt(textField.getText())>0 && Integer.parseInt(textField.getText())<geld ) {
return true;
}
} catch (NumberFormatException e) {
return false;
}
}
return false;
}
now I disagree with this solution, because your code shouldn't depend on an error, or am I getting this wrong? so i sat down and wrote this:
public static boolean checkInput(JTextField textField, int spielerGeld, String eingabe) {
boolean matched = false;
switch (eingabe) {
case "num":
if (!textField.getText().isEmpty() && textField.getText().matches("^[0-9]*$")) {
int geldinput = Integer.parseInt(textField.getText());
if (geldinput > 0 && geldinput < spielerGeld) {
matched = true;
}
}
break;
case "string":
if (!textField.getText().isEmpty() && textField.getText().matches("^[a-zA-Z]*$")) {
matched = true;
}
break;
default:
break;
}
return matched;
}
Keep in mind, we yet dont have any textfields we have to check, but I just implemented it to get a grasp of how you could do multiple checks within one method.
So now my question is, what code is "better"? and what could we/I do better?
Thanks in advance!
EDIT1:
So as some already have mentioned, you say my method is not build up after the Single responsibility principle.
But if split up into 'checkInputIsnumber' and checkInputIsString' would the first solution(my friend), still be the "better" one?
EDIT2:
Better is defined as in, the method should be of low cyclomatic complexity, easy readability and be easy to maintain in the long run.
The first approach is much better than the second one.
Single responsibility: You should avoid creating methods that do more than one thing.
Open–closed principle: Your 'validation' is not extensible. Try creating a Validator interface and then an implementation per validation type.
Switch statements increase cyclomatic complexity and make testing harder.
Also, don't use textField.getText() everywhere, it's quite possible that it will change between calls. Assign it to a local variable or even better use a String as your argument and not JText. As Fildor pointed out you correctly avoid using exceptions for flow control and it is indeed better to have a single return point. Having said that, for simple cases when you just parse/check and return, it is acceptable.
You should put every check in a single function. After a while your "all in one function" will be unreadable an unmaintainable. Also it easier to change the checks if they are in single functions. Using try/catch for control flow is no good idea. It is expensive at runtime. It is not a good style and most developers won't expect control flow in a catch block.Excpetions are for exceptional situations.

Are there any similar statements that do the same job as the Switch Statement?

I am a beginner, and I use the switch statement a lot. Sometimes it becomes quite annoying to use it. Are there any other statements in Java that can do a similar job like the switch statement?
A drop-in alternative is a block of if, else if, else.
This can be preferable to a switch if the corresponding case expressions are not compile-time evaluable. Purists like me dislike switching on strings in Java, and C# seems to be moving to a paradigm where you can switch on pretty much anything.
You can also put the more frequently occurring cases towards the top of an if block, and knock out conditions early on that could cause later tests to fail, such as checking objects for null.
You can also consider using a chain of ternary conditional, but note that they have curious type promotion rules in Java.
you can use an if-structure
if (condition) {
// do this
} else if (condition2) {
// do this
} else {
// do something else
}
You can leave out the else-if or else part if you like
Actually it depends on situation if you have only two cases then you can go for the Ternary operator ('?').
But Yes, there is alternative you can do refactoring by using polymorphism and inheritance.
Like this,
// *** With switch ***
class Car {
//...
double getSpeed() {
switch (type) {
case BMW:
return 250;
case AUDI:
return 200;
case SUZUKI:
return 300;
}
}
}
// *** Without switch *** Create Abstract class
abstract class Car {
abstract double getSpeed();
}
class BMW extends Car {
double getSpeed() {
return 250;
}
}
class Audi extends Car {
double getSpeed() {
return 200;
}
}
class suzuki extends Car {
double getSpeed() {
return 300;
}
}
// simple use
speed = Car.getSpeed();
There are many ways to achieve things - it highly depends on the situation.
The obvious one is if-else, but sometimes, a Map is a better option.
See this question: How is this design pattern called? (map instead of switch)

What is the best way to handle code with if/switch statements having high cyclomatic complexity?

I have a method that takes an input called capability id. Based on the capability id, I execute the business implementations put forward in form of a switch case. I have my functionality working, But came across a problem where the sonar report is showing high cyclomatic complexity around 12-14. The company I work for, uses a standard of 10 as the max cyclomatic complexity. My considerations is that, If I happen to break the code into too many block, the code readability is getting affected.
Note:-
I don't have permissions to change sonar rules.
Unfortunately, I can't share the code. The code would be in the following way though,
// Dependency Injected
private someService;
public void processCapability(..., String capabilityId) {
switch (capabilityId) {
case ORDER_DISPENSED_WITH_SOURCE1:
someService.doDispense1();
case ORDER_DISPENSED_WITH_SOURCE2:
someService.doDispense2();
case ORDER_REJECTED:
someService.doReject();
case ORDER_CANCEL:
someService.doCancel();
case ORDER_PURGE:
someService.doPurge();
...
default: throw exception
}
}
Update: I was able to resolve my problem, with a solution I posted in below answers.
There are multiple ways to avoid large if-else statements.
Maybe the most common patterns are the template method pattern and the strategy pattern.
As you haven't provided any code, it's hard to help you to lower the cyclomatic complexity, but I'm absolutely sure, there's a way to avoid those if-else-s.
I'd suggest you to read a bit about the topic; I'll provide you some sources I've found useful:
Using strategy to replace if else
Using command pattern to replace conditional logic
I'd encourage you to use enums, they are more usable in conditional logic than you'd think: using enums instead of switch
Also it's good to know about various design patterns, maybe you can find a suitable one here
Also a readable code means it's easy to understand. I agree with #Erwin Bolwidt's comment, if you create more methods with good names, it will be easier to read. To read more about the topic, check the book "Clean Code" by Robert C. Martin.
I suggest that you refactor this switch statement (which is a code smell) using polymorphism. A possible way to do it is something like that:
public interface Order {
void processCapability(...);
}
public final class RejectedOrder implements Order {
private final SomeService someService;
public RejectedOrder(SomeService someService) {
this.someService = someService;
}
#Override
public void processCapability(...) {
someService.doReject();
}
}
public final class CancelledOrder implements Order {
private final SomeService someService;
public RejectedOrder(SomeService someService) {
this.someService = someService;
}
#Override
public void processCapability(...) {
someService.doCancel();
}
}
More useful resources talking about the 'switch smell':
http://refactoring.com/catalog/replaceConditionalWithPolymorphism.html
http://c2.com/cgi/wiki?SwitchStatementsSmell
If there is nothing wrong with your code, why make it more complex only to avoid SonarQube warnings? You might not be allowed to change SonarQube rules, but you certainly can suppress the warning by annotating your method with the following:
#SuppressWarnings("squid:MethodCyclomaticComplexity")
I have figured one way to handle the big if/switch statements. Based on the ideas, I got from everyone of you. I compiled into a simple solution that is easy to understand and modify, also handles the complexity part. Please find my solution below,
// Dependency Injected
private someService;
public void processCapability(..., String capabilityId) {
Boolean isCapabilityProcessed = processDispenseCapabilities(...) || processUpdateCapabilities(..);
if(isCapabilityProcessed) {
throw exception("Invalid Capability");
}
}
private Boolean processDispenseCapabilities(..,String capabilityId) {
Boolean result = false;
switch (capabilityId) {
case ORDER_DISPENSED_WITH_SOURCE1:
someService.doDispense1();
result = true;
case ORDER_DISPENSED_WITH_SOURCE2:
someService.doDispense2();
result = true;
case ORDER_REJECTED:
someService.doReject();
result = true;
...
default: //do nothing
}
return result;
}
private Boolean processUpdateCapabilities(..,String capabilityId) {
Boolean result = false;
switch (capabilityId) {
case ORDER_CANCEL:
someService.doCancel();
result = true;
case ORDER_PURGE:
someService.doPurge();
result = true;
...
default: //do nothing
}
return result;
}

How to remove large if-else-if chain [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Long list of if statements in Java
I was tasked to work with some code, and there is a giant if-else-if chain (100+ else-ifs) that checks Strings.
What are some good techniques to update this code as to where the if-else-if chain can be shrunken down to something much more manageable.
The chain looks something like this:
if(name.equals("abc")){
do something
} else if(name.equals("xyz")){
do something different
} else if(name.equals("mno")){
do something different
} ......
.....
else{
error
}
You can extract the code in each branch to a separate method, then turn the methods into implementations of a common base interface (let's call it Handler). After that, you can fill a Map<String, Handler> and just look up and execute the right handler for given string.
Unfortunately the implementation of 100+ subclasses for the interface requires quite a lot of boilerplate code, but currently there is no simpler way in Java to achieve this. Implementing the cases as elements of an Enum may help somewhat - here is an example. The ideal solution would be using closures / lambdas, but alas we have to wait till Java 8 for that...
Some options / ideas:
Leave it as it is - it's not fundamentally broken, and is reasonably clear and simple to maintain
Use a switch statement (if you are using Java 7) - not sure if this gains you much though
Create a HashMap of String to FunctionObjects where the function objects implement the required behaviour as a method. Then your calling code is just: hashMap.get(name).doSomething();
Break it into a heirarchy of function calls by sub-grouping the strings. You could do this by taking each letter in turn, so one branch handles all the names starting with 'a' etc.
Refactor so that you don't pass the name as a String but instead pass a named object. Then you can just do namedObject.doSomething()
With Enums, you can have a method per instance.
public enum ActionEnum {
ABC {
#Override
void doSomething() {
System.out.println("Doing something for ABC");
}
},
XYZ {
#Override
void doSomething() {
System.out.println("Doing something for XYZ");
}
};
abstract void doSomething();
}
public class MyActionClass {
public void myMethod(String name) {
ActionEnum.valueOf("ABC").doSomething();
}
}
It is still kinda messy (big enum with 100+ entries, even it all it does is dispatching), but may avoid the HashMap initialization code (100+ puts is also messy in my opinion).
And yet another option (for documentation purposes) would be reflection:
public interface Action {
void doSomething();
}
public class ABCAction implements Action {
#Override
public void doSomething() {
System.out.println("Doing something for ABC");
}
}
public class MyActionClass {
void doSomethingWithReflection(String name) {
try {
Class<? extends Action> actionClass = Class.
forName("actpck."+ name + "Action").asSubclass(Action.class);
Action a = actionClass.newInstance();
a.doSomething();
} catch (Exception e) {
// TODO Catch exceptions individually and do something useful.
e.printStackTrace();
}
}
}
Each approach has it's trade offs:
HashMap = Fast + Kinda messy ("set-up" code with hundred of puts)
Enum = Fast + Kinda messy 2 (huge file).
Reflection = Slower + runtime error prone, but provides clean separation without resorting to clunky big HashMap.
Like Matt Ball said in his comment, you can use a command pattern. Define a collection of Runnable classes:
Runnable task1 = new Runnable() {
public void run() { /* do something */ }
};
Runnable task2 = // etc.
Then you can use a map from your keys to runnables:
Map<String,Runnable> taskMap = new HashMap<String,Runnable>();
taskMap.put("abc", task1);
taskMap.put("xyz", task2);
// etc.
Finally, replace the if-else chain with:
Runnable task = taskMap.get(name);
if (task != null) {
task.run();
} else {
// default else action from your original chain
}
you can use the switch statement , but Switch statements with String cases have been implemented in Java SE 7
the best solution is to use the command pattern
This is a popular Arrow Anti-Pattern and Jeff discusses some approaches to handle this very nicely in his post here.

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