lambda函数的引用在哪里? - java

我试图确切地了解lambda和高阶函数在现代Java的JVM级别上如何工作。我写了这个简单的测试类:

public final class Main {
    public static void main(String[] args) {
        var s = new Object[] { 1.0, 2.0, 3.0 };
        System.out.println(sum(s, x -> 1000000.0));
    }

    public static double sum(Object[] s, Function<Object, Double> f) {
        var r = 0.0;
        for (var a : s) {
            r += f.apply(a);
        }
        return r;
    }
}

编译为此:

Compiled from "Main.java"
public final class prover.Main {
  public prover.Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static double sum(java.lang.Object[], java.util.function.Function<java.lang.Object, java.lang.Double>);
    Code:
       0: dconst_0
       1: dstore_2
       2: aload_0
       3: astore        4
       5: aload         4
       7: arraylength
       8: istore        5
      10: iconst_0
      11: istore        6
      13: iload         6
      15: iload         5
      17: if_icmpge     50
      20: aload         4
      22: iload         6
      24: aaload
      25: astore        7
      27: dload_2
      28: aload_1
      29: aload         7
      31: invokeinterface #7,  2            // InterfaceMethod java/util/function/Function.apply:(Ljava/lang/Object;)Ljava/lang/Object;
      36: checkcast     #13                 // class java/lang/Double
      39: invokevirtual #15                 // Method java/lang/Double.doubleValue:()D
      42: dadd
      43: dstore_2
      44: iinc          6, 1
      47: goto          13
      50: dload_2
      51: dreturn

  public static void main(java.lang.String[]);
    Code:
       0: iconst_3
       1: anewarray     #2                  // class java/lang/Object
       4: dup
       5: iconst_0
       6: dconst_1
       7: invokestatic  #19                 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
      10: aastore
      11: dup
      12: iconst_1
      13: ldc2_w        #23                 // double 2.0d
      16: invokestatic  #19                 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
      19: aastore
      20: dup
      21: iconst_2
      22: ldc2_w        #25                 // double 3.0d
      25: invokestatic  #19                 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
      28: aastore
      29: astore_1
      30: getstatic     #27                 // Field java/lang/System.out:Ljava/io/PrintStream;
      33: aload_1
      34: invokedynamic #33,  0             // InvokeDynamic #0:apply:()Ljava/util/function/Function;
      39: invokestatic  #36                 // Method sum:([Ljava/lang/Object;Ljava/util/function/Function;)D
      42: invokevirtual #42                 // Method java/io/PrintStream.println:(D)V
      45: return

  private static java.lang.Double lambda$main$0(java.lang.Object);
    Code:
       0: ldc2_w        #48                 // double 1000000.0d
       3: invokestatic  #19                 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
       6: areturn
}

现在,lambda函数本身最后被编译为私有静态方法,这已经足够清楚了。但是在哪里提到呢?调用sum的代码似乎是:

      33: aload_1
      34: invokedynamic #33,  0             // InvokeDynamic #0:apply:()Ljava/util/function/Function;
      39: invokestatic  #36                 // Method sum:([Ljava/lang/Object;Ljava/util/function/Function;)D

那以某种方式指代lambda函数吗?如果是这样,怎么办?有什么参考?

参考方案

使用javap -p -v将产生一个标记为BootstrapMethods的部分,其中列出了用于初始化lambda的所有引导方法:

BootstrapMethods:
  0: #41 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #42 (Ljava/lang/Object;)Ljava/lang/Object;
      #43 REF_invokeStatic Scratch.lambda$main$0:(Ljava/lang/Object;)Ljava/lang/Double;
      #44 (Ljava/lang/Object;)Ljava/lang/Double;

其中包含对实现实际代码的方法的引用(在我的情况下为Scratch.lambda$main$0,确切名称将根据编译器供应商/版本/标志的不同而有所不同)。

请注意,有意将Class文件中的表示形式保持在相当高的水平(有一些引导程序方法,这些方法返回要在运行时执行的实际代码)。这意味着JVM在如何实现和优化它方面没有很多限制。这也意味着研究字节码只会告诉您太多信息,因为JVM可以相当自由地解释它在那里看到的内容。

Java:线程主java.lang.NoClassDefFoundError中的异常 - java

我正在尝试使Red5 Flash Media Server在我的计算机上工作。我已经安装了它,但是在运行服务器时出现此错误:- Exception in thread "main" java.lang.NoClassDefFoundError: org/red5/server/Bootstrap Caused by: java.lang.…

不兼容的类型:java.lang.Object无法转换为T - java

这是我的代码:package datastructures; import java.util.Iterator; public class Stack<T>{ private class Node<T>{ T data; Node next; } private int size; private Node head; privat…

java:继承 - java

有哪些替代继承的方法? java大神给出的解决方案 有效的Java:偏重于继承而不是继承。 (这实际上也来自“四人帮”)。他提出的理由是,如果扩展类未明确设计为继承,则继承会引起很多不正常的副作用。例如,对super.someMethod()的任何调用都可以引导您通过未知代码的意外路径。取而代之的是,持有对本来应该扩展的类的引用,然后委托给它。这是与Eric…

Java:BigInteger,如何通过OutputStream编写它 - java

我想将BigInteger写入文件。做这个的最好方式是什么。当然,我想从输入流中读取(使用程序,而不是人工)。我必须使用ObjectOutputStream还是有更好的方法?目的是使用尽可能少的字节。谢谢马丁 参考方案 Java序列化(ObjectOutputStream / ObjectInputStream)是将对象序列化为八位字节序列的一种通用方法。但…

Java-如何将此字符串转换为日期? - java

我从服务器收到此消息,我不明白T和Z的含义,2012-08-24T09:59:59Z将此字符串转换为Date对象的正确SimpleDateFormat模式是什么? java大神给出的解决方案 这是ISO 8601标准。您可以使用SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy-MM…