从编译时到字节码生成的Java双冒号运算符?
在本文中,作者使用了以下示例:从编译时到字节码生成的Java双冒号运算符?,java,lambda,java-8,Java,Lambda,Java 8,在本文中,作者使用了以下示例: @Override public final OptionalInt max() { return reduce(Math::max); //this is the gotcha line } 因此,在本例中,max()似乎是该类实例上Math.max的代理。但是,没有参数传递给max,所以java 8会将其编译为(伪代码): 还有,如何为类似这样的东西编写javadoc?在Java 8之前,应该编写为: public MyMathInteface {
@Override
public final OptionalInt max() {
return reduce(Math::max); //this is the gotcha line
}
因此,在本例中,max()
似乎是该类实例上Math.max
的代理。但是,没有参数传递给max,所以java 8会将其编译为(伪代码):
还有,如何为类似这样的东西编写javadoc?在Java 8之前,应该编写为:
public MyMathInteface {
OptionalInt max(Integer a, Integer b);
}
public static final MyMathInterface reducing = new MyMathInterface() {
@Override
public OptionalInt max(Integer a, Integer b) {
return OptionalInt.of(Math.max(a, b));
}
};
@Override
public final OptionalInt max() {
return reduce(reducing);
}
那么reduce将被定义为:
public static OptionalInt reduce(MyMathInteface toReduce) {
return toReduce.max(someValueA, someValueB);
}
因此,为了回答您的问题,不会将任何参数传递给Math::max
,因为这些值是由reduce
函数检索的。它们可以是常量,也可以从其他地方检索
在任何情况下,以这种方式使用max方法称为方法引用,也就是说,您可以在这里执行
SomeObject::method
。操作符创建一个方法引用。它返回一个函数,但不调用该函数。用户(reduce)负责调用该函数。在Java 8之前,这将被编写为:
public MyMathInteface {
OptionalInt max(Integer a, Integer b);
}
public static final MyMathInterface reducing = new MyMathInterface() {
@Override
public OptionalInt max(Integer a, Integer b) {
return OptionalInt.of(Math.max(a, b));
}
};
@Override
public final OptionalInt max() {
return reduce(reducing);
}
那么reduce将被定义为:
public static OptionalInt reduce(MyMathInteface toReduce) {
return toReduce.max(someValueA, someValueB);
}
因此,为了回答您的问题,不会将任何参数传递给Math::max
,因为这些值是由reduce
函数检索的。它们可以是常量,也可以从其他地方检索
在任何情况下,以这种方式使用max方法称为方法引用,也就是说,您可以在这里执行SomeObject::method
。操作符创建一个方法引用。它返回一个函数,但不调用该函数。用户(reduce)负责调用该函数
因此,在本例中,max()
似乎是该类实例上Math.max
的代理。但是,没有参数传递给max,所以java 8会将其编译为(伪代码):
不完全是:)。让我们从计算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
知道如何扩展该逻辑,将整个序列减少为一个数字。它为您处理边缘案例和聚合。它所需要的只是一个函数,它接收两个数字并返回一个。该函数应符合函数接口IntBinaryOperator
功能接口是用于描述单个功能的接口。具体来说,它描述了参数类型和返回类型。其余的大部分都是多余的。IntBinaryOperator
的签名如下所示:
int applyAsInt(int left, int right);
stream.reduce(
new IntBinaryOperator() {
public int applyAsInt(int a, int b) {
return b > a ? b : a;
}
}
);
您可以通过多种方式提供符合此规范的函数。在Java 8之前,您可能做过如下操作:
int applyAsInt(int left, int right);
stream.reduce(
new IntBinaryOperator() {
public int applyAsInt(int a, int b) {
return b > a ? b : a;
}
}
);
Java8为我们提供了一种称为lambda表达式的函数接口的简写形式。它们更加简洁,虽然它们在概念上类似于匿名内部类,但它们并不完全相同
stream.reduce((a, b) -> b > a ? b : a);
上述两个函数都是等价的:它们接受两个数字并返回两个数字中较大的一个。事实证明,每个标准编程库都有一个函数,它做的事情完全相同。在Java中,该函数是Math.max
。因此,我可以委托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
的包装类,该类在实现applyAsInt
时委托给Math.max
。它只执行此“引导”步骤一次。由于调用Math.max
只依赖于传入的两个数字,因此它可以缓存该实现,并在下次使用此代码路径时使用它
因此,在本例中,max()
似乎是该类实例上Math.max
的代理。但是,没有参数传递给max,所以java 8会将其编译为(伪代码):
不完全是:)。让我们从计算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
知道如何扩展该逻辑,将整个序列减少为一个数字。它为您处理边缘案例和聚合。它所需要的只是一个函数,它接收两个数字并返回一个。该函数应符合函数接口IntBinaryOperator
功能接口是用于描述单个功能的接口。具体来说,它描述了参数类型和返回类型。其余的大部分都是多余的。IntBinaryOperator
的签名如下所示:
int applyAsInt(int left, int right);
stream.reduce(
new IntBinaryOperator() {
public int applyAsInt(int a, int b) {
return b > a ? b : a;
}
}
);
您可以通过多种方式提供符合此规范的函数。在J之前