正如你们许多人所知,有一个Operation
enum的经典例子(现在使用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;
}
}
经过测试:
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方式是首选的方式吗?
答案 0 :(得分:21)
显然,lambda版本更具可读性。它不仅更短,而且允许读者在构造函数中第一眼看到实现操作符。想象一下,您希望将enum
扩展为支持int
计算......
从性能的角度来看,您正在通过生成的lambda类交换匿名enum
内部类。 lambda版本增加了另一级别的委派,但这对HotSpot优化器没有任何挑战。不太可能看到有关执行性能的任何差异。
但是,在应用lambda模式时,您可能会使用该类获得应用程序的 startup 的加速。原因是对于传统的专用enum
方法,Java编译器必须为每个案例生成一个内部类,该内部类存在于文件系统中或(可能是zip压缩的)Jar文件中。在运行中生成lambda类(具有非常简单的结构)的字节代码通常比加载类更快。也许有助于生成的lambda类没有访问权限检查。
总结一下:
所以这对lambda来说是个大赢家。是的,我认为lambda方式是Java 8的首选方式。
答案 1 :(得分:18)
这取决于您如何定义更好。
在你的情况下,在我看来,lambdas是纯粹的胜利。您甚至可以重用一些现有的JDK函数,例如:
enum Operation implements DoubleBinaryOperator {
PLUS ("+", Double::sum),
...
}
这简短易读。我不认为可以说任何合理的性能,而不用基准代码。
Lambda用invokeDynamic
实现,以动态地将呼叫站点链接到要执行的实际代码;没有匿名的内部阶级。
答案 2 :(得分:5)
定义更糟糕,很可能它使用更多字节代码并且稍微慢一些。
除非这些对您很重要,否则我会使用您认为更清晰,更简单的方法。
答案 3 :(得分:2)
我很惊讶没有人提到这一点,但是在实现多种方法时,lambda方法可能会变得毛茸茸。将一堆无名的lambda传递给构造函数将更简洁,但不一定更具可读性。
此外,随着函数大小的增加,使用lambda的好处会减少。如果你的lambda长度超过几行,那么覆盖可能同样容易阅读,如果不容易的话。