Java 经典操作枚举示例中的 Lambda
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23361418/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Lambdas in the classical Operation enum example
提问by skiwi
As many of you may know, there is a classical example of the Operation
enum (using Java 8 standard interface now though), that is the following:
很多人可能都知道,有一个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;
}
}
Tested with:
测试:
Arrays.stream(Operation.values())
.forEach(op -> System.out.println("Performing operation " + op.getSymbol() + " on 2 and 4: " + op.applyAsDouble(2, 4)));
It provides:
它提供:
Performing operation + on 2 and 4: 6.0
Performing operation - on 2 and 4: -2.0
Performing operation * on 2 and 4: 8.0
Performing operation / on 2 and 4: 0.5
执行操作 + 在 2 和 4 上:6.0
执行操作 - 在 2 和 4 上:-2.0
执行操作 * 在 2 和 4 上:8.0
执行操作/在 2 和 4 上:0.5
But I feel like we can do better with Java 8, hence I implemented the following:
但我觉得我们可以用 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);
}
}
Functionally it is equivalent, however are both implementations still similar, or are there some hidden details that make the new version worse as the old version?
在功能上它是等效的,但是两者的实现仍然相似,还是有一些隐藏的细节使新版本不如旧版本更糟糕?
And lastly, is the lambda way the preferred way to do it as of Java 8?
最后,从 Java 8 开始,lambda 方式是首选的方式吗?
采纳答案by Holger
Obviously, the lambda version is far more readable. Not only is it shorter, it allows a reader to see the the implementation operator at the first glance in the constructor. Imagine you want to extend the enum
to support int
calculation as well…
显然,lambda 版本更具可读性。它不仅更短,而且允许读者第一眼看到构造函数中的实现运算符。想象一下,您还想扩展enum
以支持int
计算……
From a performance point of view you are exchanging the anonymous enum
inner classes by generated lambda classes. The lambda version adds another level of delegation but that's no challenge to the HotSpot optimizer. It's unlikely to see any difference regarding the execution performance.
从性能的角度来看,您正在enum
通过生成的 lambda 类交换匿名内部类。lambda 版本增加了另一个级别的委托,但这对 HotSpot 优化器没有挑战。不太可能在执行性能方面看到任何差异。
However, when applying the lambda pattern consequently you might get a speedup of the startupof applications using the class. The reason is that for the traditional specialized enum
approach the Java compiler has to generate an inner class for each case which resides either in the file system or (possibly zip-compressed) in a Jar file. Generating the byte code for a lambda class (which has a very simple structure) on the fly is usually faster than loading a class. It might help as well that there is no access checking for the generated lambda classes.
但是,当应用 lambda 模式时,您可能会加速使用该类的应用程序启动。原因是,对于传统的专用enum
方法,Java 编译器必须为驻留在文件系统或(可能是 zip 压缩的)Jar 文件中的每种情况生成一个内部类。动态生成 lambda 类(具有非常简单的结构)的字节码通常比加载类更快。没有对生成的 lambda 类进行访问检查也可能有所帮助。
To summarize it:
总结一下:
- The lambda approach is easier to read and its code is more maintainable (the big point)
- The execution performance is roughly the same
- The startup time might be shorter for the lambda approach
- lambda 方法更易于阅读,其代码更易于维护(重点)
- 执行性能大致相同
- lambda 方法的启动时间可能更短
So it's a big win for the lambda. Yes, I think the lambda way the preferred way to do it as of Java 8.
所以这对 lambda 来说是一个巨大的胜利。是的,我认为 lambda 方式是从 Java 8 开始的首选方式。
回答by Peter Lawrey
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.
除非这些对你很重要,否则我会使用你觉得更清晰、更简单的方法。
回答by emesx
It depends how you define better.
这取决于你如何定义更好。
In your case and in my opinion, the lambdas are a pure win. You can even reuse some existing JDK functions, e.g.:
在你的情况下,在我看来,lambdas 是一个纯粹的胜利。您甚至可以重用一些现有的 JDK 函数,例如:
enum Operation implements DoubleBinaryOperator {
PLUS ("+", Double::sum),
...
}
This is short and readable. I don't think anything reasonable can be said about performance w/o benchmarking your code.
这是简短易读的。我认为在没有对代码进行基准测试的情况下可以说任何合理的性能。
Lambdas are implemented with invokeDynamic
to dynamically link call site to actual code to execute; no anonymous, inner classes.
实现 LambdainvokeDynamic
以将调用站点动态链接到要执行的实际代码;没有匿名的内部类。
回答by shmosel
I'm surprised no one mentioned this, but the lambda approach can get hairy when implementing multiple methods. Passing a bunch of nameless lambdas into a constructor will be more concise, but not necessarily more readable.
我很惊讶没有人提到这一点,但是 lambda 方法在实现多种方法时会变得很麻烦。将一堆无名 lambda 传递给构造函数会更简洁,但不一定更易读。
Also, the benefit of using a lambda decreases as the function grows in size. If your lambda is more than several lines long, an override might be just as easy to read, if not easier.
此外,使用 lambda 的好处随着函数大小的增长而减少。如果你的 lambda 超过几行,覆盖可能同样容易阅读,如果不是更容易的话。