Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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();
    }
}
like image 783
Ignacio Tomas Crespo Avatar asked Mar 08 '23 00:03

Ignacio Tomas Crespo


2 Answers

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;
}
like image 112
Jacob G. Avatar answered Mar 14 '23 22:03

Jacob G.


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);
like image 33
Michael Avatar answered Mar 14 '23 23:03

Michael