经典操作枚举示例中的 Lambda

2022-09-01 02:35:11

正如你们中的许多人可能知道的那样,有一个经典的枚举示例(尽管现在使用Java 8标准接口),如下所示:Operation

enum Operation implements DoubleBinaryOperator {
    PLUS("+") {
        @Override
        public double applyAsDouble(final double left, final double right) {
            return left + right;
        }
    },
    MINUS("-") {
        @Override
        public double applyAsDouble(final double left, final double right) {
            return left - right;
        }
    },
    MULTIPLY("*") {
        @Override
        public double applyAsDouble(final double left, final double right) {
            return left * right;
        }
    },
    DIVIDE("/") {
        @Override
        public double applyAsDouble(final double left, final double right) {
            return left / right;
        }
    };

    private final String symbol;

    private Operation(final String symbol) {
        this.symbol = symbol;
    }

    public String getSymbol() {
        return symbol;
    }
}

测试依据:

Arrays.stream(Operation.values())
        .forEach(op -> System.out.println("Performing operation " + op.getSymbol() + " on 2 and 4: " + op.applyAsDouble(2, 4)));

它提供:

执行操作 + 在 2 和 4 上:6.0
执行操作 - 在 2 和 4 上:-2.0
在 2 和 4 上执行操作 *:8.0
执行操作/在 2 和 4 上执行操作:0.5

但我觉得我们可以在Java 8上做得更好,因此我实现了以下内容:

enum Operation implements DoubleBinaryOperator {
    PLUS    ("+", (l, r) -> l + r),
    MINUS   ("-", (l, r) -> l - r),
    MULTIPLY("*", (l, r) -> l * r),
    DIVIDE  ("/", (l, r) -> l / r);

    private final String symbol;
    private final DoubleBinaryOperator binaryOperator;

    private Operation(final String symbol, final DoubleBinaryOperator binaryOperator) {
        this.symbol = symbol;
        this.binaryOperator = binaryOperator;
    }

    public String getSymbol() {
        return symbol;
    }

    @Override
    public double applyAsDouble(final double left, final double right) {
        return binaryOperator.applyAsDouble(left, right);
    }
}

在功能上它是等效的,但是两种实现是否仍然相似,或者是否有一些隐藏的细节使新版本比旧版本更糟?

最后,从Java 8开始,lambda方式是否是首选方式?


答案 1

显然,lambda 版本更具可读性。它不仅更短,还允许读者在构造函数中乍一看实现运算符。想象一下,您还想扩展以支持计算...enumint

从性能的角度来看,您正在通过生成的 lambda 类交换匿名内部类。lambda 版本增加了另一个级别的委派,但这对 HotSpot 优化器来说不是挑战。在执行性能方面不太可能看到任何差异。enum

但是,在应用 lambda 模式时,您可能会使用该类加快应用程序的启动速度。原因是,对于传统的专用方法,Java编译器必须为每个情况生成一个内部类,该类要么驻留在文件系统中,要么(可能是zip压缩的)在Jar文件中。动态为 lambda 类(具有非常简单的结构)生成字节码通常比加载类更快。对生成的 lambda 类没有访问检查可能也会有所帮助。enum

总结一下:

  • lambda方法更易于阅读,其代码更易于维护(重点)
  • 执行性能大致相同
  • 对于 lambda 方法,启动时间可能更短

所以这对lambda来说是一个巨大的胜利。是的,我认为 lambda 方式是 Java 8 的首选方法。


答案 2

这取决于你如何更好地定义。

在你的情况下,在我看来,lambdas是纯粹的胜利。您甚至可以重用一些现有的 JDK 函数,例如:

enum Operation implements DoubleBinaryOperator {
    PLUS    ("+", Double::sum),
    ...
}

这简短且可读。我不认为任何合理的关于性能的说法,没有你的代码基准测试。

Lambdas的实现方式是动态链接调用站点到要执行的实际代码;没有匿名的内部类。invokeDynamic


推荐