Java:用lambda替换switch。值得吗?

4

在检查事件时,使用switch或if代码块是常见的做法。当它被简化时,可以是清晰易懂的代码,但仍然似乎比必要的行数更多,并且可以使用lambda表达式进行简化。

if代码块:

if(action == ACTION_1){
    doAction1();
} else if(action == ACTION_2){
    doAction2();
} else {
    doDefaultAction();
}

带有 switch 的代码块:

switch(action){
    case ACTION_1:
        doAction1();
        break;
    case ACTION_2:
        doAction2();
        break;
    default:
        doDefaultAction();
}

使用以下实用类 With 来使用 Lambda 表达式的块:
with(action)
    .when(ACTION_1, this::doAction1)
    .when(ACTION_2, this::doAction2)
    .byDefault(this::doDefaultAction)

使用lambda的代码量更少,但问题是:它比其他方法更易读吗?更易于维护?在性能方面,lambda是最差的,但对于性能不重要的情况下,使用lambda版本比开关/ if块更短。
那么,你怎么看?也许有一种Kotlin方法比这更短,我只专注于Java,我喜欢Kotlin,但它的编译速度对我的项目来说仍然太慢了。
当块必须返回特定值时,可以使用类似的实用程序类。
FYI,用于lambda的类在这里,我没有检查错误,只是为了这个示例快速制作的:
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();
    }
}

此外,“id == expectedId”是个非常糟糕的想法。你知道为什么… - Boris the Spider
感谢您的评论。我同意,这种方法效率较低,并且对于对象来说使用==比较不好,但是它与switch做的事情相同,因此在这种情况下比较应该不是问题。 - Ignacio Tomas Crespo
“但它与switch语句做的一样” 冒泡排序和其他排序算法做的一样;但是当你调用Arrays.sortCollections.sort时,Java不使用冒泡排序... 并非所有正确的算法都是相等的。 - Andy Turner
错误 - 这就是为什么我说这是一个可怕的想法。虽然 switch 会 拆箱 基本类型,但泛型会 装箱 它们 - 这意味着即使任何 -128 < int < 127 也不会按照您的期望工作。别忘了 switch 支持 String - Boris the Spider
你是对的,它们不完全相同,但它们以不同的方式执行相同的功能。关于泛型,关于装箱的评论很有趣,应该考虑到比较是通过引用使用 if_acmpne 指令进行的,但必须确保它使用缓存来处理频繁出现的值。谢谢! - Ignacio Tomas Crespo
显示剩余2条评论
4个回答

3

正如一些人所说,用复合方法替换switch不太高效。根据您的使用情况,甚至值得使用您自己的实现。

有趣的是,Oracle实际上正在计划在switch语句中实现lambda表达式,就像这个最近的JEP中所看到的那样。

例子:

String formatted = switch (s) {
    case null -> "(null)";
    case "" -> "(empty)";
    default -> s;
}

1
你关于性能的看法是正确的,非常感谢提供 JEP 的链接!太棒了。 - Ignacio Tomas Crespo
1
LISP自50多年以来已经拥有了像样的表达式条件结构(不仅仅是语句),现在这种特性终于也要应用到Java中了。这将会是一个可读性更高、更加灵活的三元运算符替代方案! - Ralf Kleberhoff
@RalfKleberhoff 哈利路亚! - Jacob G.
1
@IgnacioTomasCrespo 还有一个你的构造的缺点:编译器非常聪明,能够理解 ifswitch 语句的控制流程:它们可以识别死代码、警告不完整的 case 列表等等。我认为这对于你的类来说是不可能的。 - Ralf Kleberhoff
@RalfKleberhoff 很好的观点,还有一个需要考虑的缺点。谢谢! - Ignacio Tomas Crespo

2

用lambda表达式替换switch操作,值得吗?

不值得。

因为在面向对象语言中,代替 switchif/else 的级联操作的解决方案是多态性(polymorphism),而不是 "fluent API"。


完全同意多态和switch语句的重要性。应该是首先考虑的事情。但在我看来,在某些情况下,这更像样板代码,而且这种模式并没有帮助。 - Ignacio Tomas Crespo
@IgnacioTomasCrespo 将配置代码与执行代码分离是多态的杀手级特性,这是我个人的看法。我从未遇到过不值得付出努力的情况... - Timothy Truckle

2

使用switch语句更加灵活,可以调用带有不同数量参数的函数或者调用多个函数。您还可以更轻松地表示两个case会导致相同的操作。它更快的事实只是一个额外的好处。

所以从这个意义上说,我不确定你的With类到底添加了什么。

然而,switch语句只能处理有限数量的类型。也许如果您传递谓词而不是执行简单的引用相等性,那么您的With类将证明更加有用:

public With<T> when(Predicate<T> expected, Action action) {
    if (!actionFound && expected.test(id)) {
        actionFound = true;
        action.execute();
    }
    return this;
}

使用示例:

final String test = "test";

with(test)
    .when(String::isEmpty,      this::doAction1)
    .when(s -> s.length() == 3, this::doAction2)
    .byDefault(this::doDefaultAction);

你是对的,switch语句更加灵活,许多情况可以合并成一个。在这种情况下,“With”类没有任何优势。我对你使用谓词的想法很感兴趣,能给我举个例子吗? - Ignacio Tomas Crespo
1
@IgnacioTomasCrespo 当然,我已经添加了一个例子。 - Michael
哇,这变得越来越好了。我喜欢使用谓词作为条件。感谢您提供的示例! - Ignacio Tomas Crespo

1
一种实现方法是声明static final Map<T, Action> EXPECTED_ID_TO_ACTION。然后你可以使用EXPECTED_ID_TO_ACTION.getOrDefault(actionId, DEFAULT_ACTION).execute(),将丑陋的switch或多个if转化为一行代码。

switch 差很多,但比建议的方法好。 - Boris the Spider
@BoristheSpider 不同意。在 switch 中有多个 case 块(> 10)看起来很丑陋。此外,这种方法允许在运行时更改分支行为(如果需要)。 - alxg2112
确实 - 感谢@JacobG。字节码跳转表可以轻松击败一个Map - Boris the Spider
@BoristheSpider 没错,就性能而言,它仍然比switch慢得多(如果我们谈论的是合理数量的分支)。感谢您指出这一点,答案已经进行了编辑。 - alxg2112

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接