java - Where is the reference to the lambda function?
问题描述
I'm trying to understand exactly how lambdas and higher order functions work at the JVM level in modern Java. I wrote this simple test class:
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;
}
}
that compiles to this:
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
}
Now, the lambda function itself gets compiled to the private static method at the end, that much is clear enough. But where is it referred to? The code to call sum
seems to be:
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
Is that somehow referring to the lambda function? If so, how? What's the reference?
解决方案
Using javap -p -v
will produce a section labeled BootstrapMethods
that lists all bootstrap methods used to initialize lambdas:
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;
This one contains references to the methods implementing the actual code (Scratch.lambda$main$0
in my case, the exact name will vary depending on compiler-vendor/-version/-flags).
Note that the representation in the Class files has intentionally been kept at a fairly high level (there are bootstrap methods that return the actual code to be executed at run time). This means that the JVM doesn't have a lot of restrictions as to how to implement and optimize this. That also means that studying the bytecode will only ever tell you so much, because the JVM can pretty freely interpret what it sees there.
推荐阅读
- azure - 将 cosmos db 中的复制活动数据工厂用作源时出错
- ios - setContentOffset 第一次不适用于 UITableViewCell 内的 UIScrollView
- sql - 将每个用户的现有角色显示到列(行到列)
- python - 如何使用 grid_remove 将放置在网格中的遗忘小部件恢复为活动状态?
- java - 将 jar 作为 docker 映像运行并在其他 java 应用程序中使用此 jar
- pdf - 为什么将PDF转换为纯文本如此困难?
- javascript - 使用扩展参数连接字符串的最有效方法
- cuda - 我的 GPU 上的总线程数、块数和网格数。
- javascript - 阻止加载混合活动内容 Mozilla 浏览器
- java - 我如何将多个数组位置及其项目传递给下一个活动并进入下一个活动