Java双冒号运算符从编译时到字节码生成?

标签 java lambda java-8

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 for Math.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/

相关文章:

Java 8 - 使用 Doclet 的自定义 JavaDoc 检查

java - 下载的 Java 7 Eclipse 无法启动

java - 检测流中的重复组

java - Spring 集成 - 如何在不轮询的情况下检查文件目录?

svn中的Java项目,结合maven

java - 在应用程序发明者或可thunkable或kodular中模拟键盘按键,例如应用程序构建网站

c++ - 为什么我在自定义比较器时收到有关空指针的错误

java - 如何让 ReSTLet 客户端忽略 SSL 证书问题

lambda - Mercury:如何声明高阶数据类型的确定性?

c# - 为什么我不能访问 DelegateCommand 的执行委托(delegate)中的实例属性?