可参考前面的文章:
匿名内部类
lambda机制
方法引用method reference
lambda、方法引用与匿名内部类比较
public class Lambda {
public static void main(String[] args) {
System.setProperty("jdk.internal.lambda.dumpProxyClasses","/root/work");
int x = 2;
IntStream stream = IntStream.of(1, 2, 3).map(i -> i * 2).map(i -> i * x);
System.out.println(Arrays.toString(stream.toArray()));
}
}
1.解语法糖desugar
Javac对Lambda表达式进行解语法糖,生成一个方法来保存Lambda表达式的内容。
该方法还会包含它所捕获的变量。、
方法引用不会额外生成方法。
i -> i * 2对应如下:
private static int lambda$main$0(int);
descriptor: (I)I
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=2, locals=1, args_size=1
0: iload_0
1: iconst_2
2: imul
3: ireturn
LineNumberTable:
line 8: 0
i -> i * x 对应如下:
private static int lambda$main$1(int, int);
descriptor: (II)I
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=2, locals=2, args_size=2
0: iload_1
1: iload_0
2: imul
3: ireturn
LineNumberTable:
line 8: 0
2.生成函数接口实现类
IntStream map(IntUnaryOperator mapper);
@FunctionalInterface
public interface IntUnaryOperator {
int applyAsInt(int operand);
...
}
根据Lambda表达式是否捕获变量,生成的适配器类以及所链接的MethodHandle均不同。
若未捕获变量,可以认为是上下文无关。启动方法将新建一个适配器类实例,生成一个特殊的方法句柄,始终返回该实例。
final class Lambda$$Lambda$1 implements java.util.function.IntUnaryOperator
minor version: 0
major version: 52
flags: ACC_FINAL, ACC_SUPER, ACC_SYNTHETIC
Constant pool:
#1 = Utf8 Lambda$$Lambda$1
#2 = Class #1 // Lambda$$Lambda$1
#3 = Utf8 java/lang/Object
#4 = Class #3 // java/lang/Object
#5 = Utf8 java/util/function/IntUnaryOperator
#6 = Class #5 // java/util/function/IntUnaryOperator
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = NameAndType #7:#8 // "<init>":()V
#10 = Methodref #4.#9 // java/lang/Object."<init>":()V
#11 = Utf8 applyAsInt
#12 = Utf8 (I)I
#13 = Utf8 Ljava/lang/invoke/LambdaForm$Hidden;
#14 = Utf8 Lambda
#15 = Class #14 // Lambda
#16 = Utf8 lambda$main$0
#17 = NameAndType #16:#12 // lambda$main$0:(I)I
#18 = Methodref #15.#17 // Lambda.lambda$main$0:(I)I
#19 = Utf8 Code
#20 = Utf8 RuntimeVisibleAnnotations
{
private Lambda$$Lambda$1();
descriptor: ()V
flags: ACC_PRIVATE
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #10 // Method java/lang/Object."<init>":()V
4: return
public int applyAsInt(int);
descriptor: (I)I
flags: ACC_PUBLIC
Code:
stack=1, locals=2, args_size=2
0: iload_1
1: invokestatic #18 // Method Lambda.lambda$main$0:(I)I
4: ireturn
RuntimeVisibleAnnotations:
0: #13()
}
如果捕获了变量,每次调用是都需更新捕获的变量。此时无法共享同一个适配器类的实例,每次调用都需新建一个适配器类实例。因此该适配器类包含一个额外的静态方法private static java.util.function.IntUnaryOperator get$Lambda(int),该方法每次新建一个适配器类实例,并将捕获的参数作为实例的域。
final class Lambda$$Lambda$2 implements java.util.function.IntUnaryOperator
minor version: 0
major version: 52
flags: ACC_FINAL, ACC_SUPER, ACC_SYNTHETIC
Constant pool:
#1 = Utf8 Lambda$$Lambda$2
#2 = Class #1 // Lambda$$Lambda$2
#3 = Utf8 java/lang/Object
#4 = Class #3 // java/lang/Object
#5 = Utf8 java/util/function/IntUnaryOperator
#6 = Class #5 // java/util/function/IntUnaryOperator
#7 = Utf8 arg$1
#8 = Utf8 I
#9 = Utf8 <init>
#10 = Utf8 (I)V
#11 = Utf8 ()V
#12 = NameAndType #9:#11 // "<init>":()V
#13 = Methodref #4.#12 // java/lang/Object."<init>":()V
#14 = NameAndType #7:#8 // arg$1:I
#15 = Fieldref #2.#14 // Lambda$$Lambda$2.arg$1:I
#16 = Utf8 get$Lambda
#17 = Utf8 (I)Ljava/util/function/IntUnaryOperator;
#18 = NameAndType #9:#10 // "<init>":(I)V
#19 = Methodref #2.#18 // Lambda$$Lambda$2."<init>":(I)V
#20 = Utf8 applyAsInt
#21 = Utf8 (I)I
#22 = Utf8 Ljava/lang/invoke/LambdaForm$Hidden;
#23 = Utf8 Lambda
#24 = Class #23 // Lambda
#25 = Utf8 lambda$main$1
#26 = Utf8 (II)I
#27 = NameAndType #25:#26 // lambda$main$1:(II)I
#28 = Methodref #24.#27 // Lambda.lambda$main$1:(II)I
#29 = Utf8 Code
#30 = Utf8 RuntimeVisibleAnnotations
{
private final int arg$1;
descriptor: I
flags: ACC_PRIVATE, ACC_FINAL
private Lambda$$Lambda$2(int);
descriptor: (I)V
flags: ACC_PRIVATE
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: invokespecial #13 // Method java/lang/Object."<init>":()V
4: aload_0
5: iload_1
6: putfield #15 // Field arg$1:I
9: return
private static java.util.function.IntUnaryOperator get$Lambda(int);
descriptor: (I)Ljava/util/function/IntUnaryOperator;
flags: ACC_PRIVATE, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
0: new #2 // class Lambda$$Lambda$2
3: dup
4: iload_0
5: invokespecial #19 // Method "<init>":(I)V
8: areturn
public int applyAsInt(int);
descriptor: (I)I
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: getfield #15 // Field arg$1:I
4: iload_1
5: invokestatic #28 // Method Lambda.lambda$main$1:(II)I
8: ireturn
RuntimeVisibleAnnotations:
0: #22()
}
附录——字节码
-private or -p
Shows all classes and members.
javap -c -v -p Lambda.class
public class Lambda
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #14.#27 // java/lang/Object."<init>":()V
#2 = String #28 // jdk.internal.lambda.dumpProxyClasses
#3 = String #29 // /root/work
#4 = Methodref #30.#31 // java/lang/System.setProperty:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
#5 = InterfaceMethodref #32.#33 // java/util/stream/IntStream.of:([I)Ljava/util/stream/IntStream;
#6 = InvokeDynamic #0:#38 // #0:applyAsInt:()Ljava/util/function/IntUnaryOperator;
#7 = InterfaceMethodref #32.#39 // java/util/stream/IntStream.map:(Ljava/util/function/IntUnaryOperator;)Ljava/util/stream/IntStream;
#8 = InvokeDynamic #1:#41 // #1:applyAsInt:(I)Ljava/util/function/IntUnaryOperator;
#9 = Fieldref #30.#42 // java/lang/System.out:Ljava/io/PrintStream;
#10 = InterfaceMethodref #32.#43 // java/util/stream/IntStream.toArray:()[I
#11 = Methodref #44.#45 // java/util/Arrays.toString:([I)Ljava/lang/String;
#12 = Methodref #46.#47 // java/io/PrintStream.println:(Ljava/lang/String;)V
#13 = Class #48 // Lambda
#14 = Class #49 // java/lang/Object
#15 = Utf8 <init>
#16 = Utf8 ()V
#17 = Utf8 Code
#18 = Utf8 LineNumberTable
#19 = Utf8 main
#20 = Utf8 ([Ljava/lang/String;)V
#21 = Utf8 lambda$main$1
#22 = Utf8 (II)I
#23 = Utf8 lambda$main$0
#24 = Utf8 (I)I
#25 = Utf8 SourceFile
#26 = Utf8 Lambda.java
#27 = NameAndType #15:#16 // "<init>":()V
#28 = Utf8 jdk.internal.lambda.dumpProxyClasses
#29 = Utf8 /root/work
#30 = Class #50 // java/lang/System
#31 = NameAndType #51:#52 // setProperty:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
#32 = Class #53 // java/util/stream/IntStream
#33 = NameAndType #54:#55 // of:([I)Ljava/util/stream/IntStream;
#34 = Utf8 BootstrapMethods
#35 = MethodHandle #6:#56 // 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;
#36 = MethodType #24 // (I)I
#37 = MethodHandle #6:#57 // invokestatic Lambda.lambda$main$0:(I)I
#38 = NameAndType #58:#59 // applyAsInt:()Ljava/util/function/IntUnaryOperator;
#39 = NameAndType #60:#61 // map:(Ljava/util/function/IntUnaryOperator;)Ljava/util/stream/IntStream;
#40 = MethodHandle #6:#62 // invokestatic Lambda.lambda$main$1:(II)I
#41 = NameAndType #58:#63 // applyAsInt:(I)Ljava/util/function/IntUnaryOperator;
#42 = NameAndType #64:#65 // out:Ljava/io/PrintStream;
#43 = NameAndType #66:#67 // toArray:()[I
#44 = Class #68 // java/util/Arrays
#45 = NameAndType #69:#70 // toString:([I)Ljava/lang/String;
#46 = Class #71 // java/io/PrintStream
#47 = NameAndType #72:#73 // println:(Ljava/lang/String;)V
#48 = Utf8 Lambda
#49 = Utf8 java/lang/Object
#50 = Utf8 java/lang/System
#51 = Utf8 setProperty
#52 = Utf8 (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
#53 = Utf8 java/util/stream/IntStream
#54 = Utf8 of
#55 = Utf8 ([I)Ljava/util/stream/IntStream;
#56 = Methodref #74.#75 // 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;
#57 = Methodref #13.#76 // Lambda.lambda$main$0:(I)I
#58 = Utf8 applyAsInt
#59 = Utf8 ()Ljava/util/function/IntUnaryOperator;
#60 = Utf8 map
#61 = Utf8 (Ljava/util/function/IntUnaryOperator;)Ljava/util/stream/IntStream;
#62 = Methodref #13.#77 // Lambda.lambda$main$1:(II)I
#63 = Utf8 (I)Ljava/util/function/IntUnaryOperator;
#64 = Utf8 out
#65 = Utf8 Ljava/io/PrintStream;
#66 = Utf8 toArray
#67 = Utf8 ()[I
#68 = Utf8 java/util/Arrays
#69 = Utf8 toString
#70 = Utf8 ([I)Ljava/lang/String;
#71 = Utf8 java/io/PrintStream
#72 = Utf8 println
#73 = Utf8 (Ljava/lang/String;)V
#74 = Class #78 // java/lang/invoke/LambdaMetafactory
#75 = NameAndType #79:#83 // 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;
#76 = NameAndType #23:#24 // lambda$main$0:(I)I
#77 = NameAndType #21:#22 // lambda$main$1:(II)I
#78 = Utf8 java/lang/invoke/LambdaMetafactory
#79 = Utf8 metafactory
#80 = Class #85 // java/lang/invoke/MethodHandles$Lookup
#81 = Utf8 Lookup
#82 = Utf8 InnerClasses
#83 = Utf8 (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;
#84 = Class #86 // java/lang/invoke/MethodHandles
#85 = Utf8 java/lang/invoke/MethodHandles$Lookup
#86 = Utf8 java/lang/invoke/MethodHandles
{
public Lambda();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 4: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=3, args_size=1
0: ldc #2 // String jdk.internal.lambda.dumpProxyClasses
2: ldc #3 // String /root/work
4: invokestatic #4 // Method java/lang/System.setProperty:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
7: pop
8: iconst_2
9: istore_1
10: iconst_3
11: newarray int
13: dup
14: iconst_0
15: iconst_1
16: iastore
17: dup
18: iconst_1
19: iconst_2
20: iastore
21: dup
22: iconst_2
23: iconst_3
24: iastore
25: invokestatic #5 // InterfaceMethod java/util/stream/IntStream.of:([I)Ljava/util/stream/IntStream;
28: invokedynamic #6, 0 // InvokeDynamic #0:applyAsInt:()Ljava/util/function/IntUnaryOperator;
33: invokeinterface #7, 2 // InterfaceMethod java/util/stream/IntStream.map:(Ljava/util/function/IntUnaryOperator;)Ljava/util/stream/IntStream;
38: iload_1
39: invokedynamic #8, 0 // InvokeDynamic #1:applyAsInt:(I)Ljava/util/function/IntUnaryOperator;
44: invokeinterface #7, 2 // InterfaceMethod java/util/stream/IntStream.map:(Ljava/util/function/IntUnaryOperator;)Ljava/util/stream/IntStream;
49: astore_2
50: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
53: aload_2
54: invokeinterface #10, 1 // InterfaceMethod java/util/stream/IntStream.toArray:()[I
59: invokestatic #11 // Method java/util/Arrays.toString:([I)Ljava/lang/String;
62: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
65: return
LineNumberTable:
line 6: 0
line 7: 8
line 8: 10
line 9: 50
line 10: 65
private static int lambda$main$1(int, int);
descriptor: (II)I
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=2, locals=2, args_size=2
0: iload_1
1: iload_0
2: imul
3: ireturn
LineNumberTable:
line 8: 0
private static int lambda$main$0(int);
descriptor: (I)I
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=2, locals=1, args_size=1
0: iload_0
1: iconst_2
2: imul
3: ireturn
LineNumberTable:
line 8: 0
}
SourceFile: "Lambda.java"
InnerClasses:
public static final #81= #80 of #84; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
0: #35 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:
#36 (I)I
#37 invokestatic Lambda.lambda$main$0:(I)I
#36 (I)I
1: #35 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:
#36 (I)I
#40 invokestatic Lambda.lambda$main$1:(II)I
#36 (I)I