在this question作者使用以下示例:
@Override
public final OptionalInt max() {
return reduce(Math::max); //this is the gotcha line
}
所以在这种情况下,它看起来好像 max()
是此类实例上 Math.max
的代理。但是,没有参数传递给 max,因此 java 8 将其编译为类似(伪代码)的内容:
@Override
public final OptionalInt max(Integer a, Integer b) {
//If neither a or b are null
return new OptionalInt.of(Math.max(a,b));
//Otherwise return empty because we can't compare the numbers
return OptionalInt.empty()
}
另外,如何为这样的事情编写 javadoc?
最佳答案
So in this case it looks as if
max()
is a proxy forMath.max
on the instance of this class. However there are no arguments passed to max, so does java 8 compile this to something like (Pseudo code):@Override public final OptionalInt max(Integer a, Integer b) { //If neither a or b are null return new OptionalInt.of(Math.max(a,b)); //Otherwise return empty because we can't compare the numbers return OptionalInt.empty() }
不完全 :)。让我们首先弄清楚reduce
运算符实际上做了什么。该文档解释说,它通过应用逻辑上等同于以下内容的算法对数字序列执行归约:
public OptionalInt reduce(IntBinaryOperator op) {
boolean foundAny = false;
int result = 0;
for (int element : [this stream]) {
if (!foundAny) {
foundAny = true;
result = element;
}
else {
result = op.applyAsInt(result, element);
}
}
return foundAny ? OptionalInt.of(result)
: OptionalInt.empty();
}
看起来很简单。如果您能告诉它如何获取两个 数字并将它们“减少”或“组合”成一个,那么reduce
就知道如何扩展该逻辑以减少一个整个序列变成一个数字。它为您处理边缘情况和聚合。它所需要的只是一个接受两个数字并返回一个数字的函数。该函数应符合函数接口(interface) IntBinaryOperator
。
函数式接口(interface) 是用来描述单个函数的接口(interface)。具体来说,它描述了参数类型和返回类型。其余的基本上是多余的。 IntBinaryOperator
的签名如下所示:
int applyAsInt(int left, int right);
您可以通过多种方式提供符合此规范的功能。在 Java 8 之前,您可能会这样做:
stream.reduce(
new IntBinaryOperator() {
public int applyAsInt(int a, int b) {
return b > a ? b : a;
}
}
);
Java 8 为我们提供了一种名为lambda 表达式 的函数式接口(interface)简写形式。它们更简洁一些,虽然它们在概念上与匿名内部类相似,但它们并不完全相同。
stream.reduce((a, b) -> b > a ? b : a);
上面的两个函数是等价的:它们接受两个数字并返回两者中较大的一个。事实证明,每个标准编程库都有一个功能完全相同。在 Java 中,该函数是 Math.max
。因此,我可以委托(delegate) Math.max
来编写此逻辑:
stream.reduce((a, b) -> Math.max(a, b));
但是等等! reduce
想要的只是一个接受两个数字并返回一个数字的函数。 Math.max
可以做到这一点,所以我什至需要将它包装在 lambda 中吗?事实证明我没有;我可以告诉 reduce
直接调用 Math.max
:
stream.reduce(Math::max);
这是说“我知道你想要一个函数,所以我告诉你按名称在哪里可以找到一个已经写好的函数”。编译器知道 Math.max
符合我们需要的 (int, int) -> int
规范,因此它发出一些字节码告诉 VM 如何“引导”它一旦需要。当 JVM 命中您对 reduce
的调用时,它会调用一个特殊方法,该方法生成一个实现 IntBinaryOperator
的包装类,该类在它的代码中委托(delegate)给 Math.max
applyAsInt
的实现。它只执行一次这个“引导”步骤。由于调用 Math.max
除了传入的两个数字之外不依赖任何其他内容,它可以缓存该实现并在您下次在此代码路径上结束时使用它。
关于Java双冒号运算符从编译时到字节码生成?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46226673/