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?