对 lambda 函数的引用在哪里?



我试图确切地理解lambdas和高阶函数在现代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在我的情况下,确切的名称将根据编译器供应商/版本/标志而有所不同(。

请注意,类文件中的表示形式有意保持在相当高的级别(有一些引导方法返回要在运行时执行的实际代码(。这意味着 JVM 在如何实现和优化这一点方面没有太多限制。这也意味着研究字节码只会告诉你这么多,因为JVM可以非常自由地解释它在那里看到的东西。

相关内容

  • 没有找到相关文章

最新更新