Lambda在经典的Operation enum例子中

ski*_*iwi 34 java lambda enums java-8

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

经测试:

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

它提供:

执行操作+ on 2和4:6.0
执行操作 - on 2和4:-2.0
执行操作*on 2和4:8.0
执行操作/ on 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

最后,lambda方式是Java 8的首选方式吗?

Hol*_*ger 25

显然,lambda版本更具可读性.它不仅更短,而且允许读者在构造函数中第一眼看到实现操作符.想象一下,你想扩展enum到支持int计算......

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

但是,在应用lambda模式时,您可能会获得使用该类启动应用程序的加速.原因是对于传统的专用enum方法,Java编译器必须为每个案例生成一个内部类,它位于文件系统中或者可能是压缩文件系统中的(可能是zip压缩的).在运行中生成lambda类(具有非常简单的结构)的字节代码通常比加载类更快.也许有助于生成的lambda类没有访问检查.

总结一下:

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

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


eme*_*esx 19

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

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

enum Operation implements DoubleBinaryOperator {
    PLUS    ("+", Double::sum),
    ...
}
Run Code Online (Sandbox Code Playgroud)

这简短易读.对于没有对代码进行基准测试的性能,我认为没有任何合理的说法.

Lambda invokeDynamic用于动态地将呼叫站点链接到要执行的实际代码; 没有匿名的内部阶级.


Pet*_*rey 5

Define worse, most likely it uses a little more byte code and is slightly slower.

Unless these are important to you, I would use the approach you find clearer and simpler.


shm*_*sel 5

我没有人对此感到惊讶,但是lambda方法在实现多种方法时可能会变得冗长。将一堆无名的lambda传递给构造函数将更加简洁,但不一定更具可读性。

同样,随着函数大小的增加,使用lambda的好处也会减少。如果您的lambda多于几行,则覆盖可能同样容易阅读,甚至不那么容易。

  • 同意,Effective Java 第三版,第 43 条:“lambda 缺乏名称和文档;如果计算不言自明或超过几行,请不要将其放入 lambda [...] 三行的 lambda 是合理的最大值。 (2认同)