您的位置:首页 > 编程语言 > Java开发

Java8 lambda表达式的实现探索

2016-01-06 16:16 465 查看
Java8引入了大家千呼万唤的lambda表达式的实现,作为码农,比较好奇的就是如何用以及如何实现的。

如何用就不用多说了,大把地方告诉你怎么用。底层如何实现的就比较有意思了。对我来说要实现lambda表达式,第一反应就是把lambda表达式转化成Java已有的实现,比如内部类,比如动态代理。那么接下来我们看看Java8里面的实现,给出个测试类

//测试类
public class Test {

public void test(){
String[] atp = {"Rafael Nadal", "Novak Djokovic",
"Stanislas Wawrinka",
"David Ferrer","Roger Federer",
"Andy Murray","Tomas Berdych",
"Juan Martin Del Potro"};
List<String> players =  Arrays.asList(atp);
players.forEach((player) -> System.out.print(player + "; "));
}

public static void main(String[] args) {
new Test().test();
}

}


对应的字节码

//字节码
E:\Developer\workspace\Test\bin>javap -v -p test.class
Classfile /E:/Developer/workspace/Test/bin/test.class
Last modified 2015-12-4; size 2020 bytes
MD5 checksum f050d6d2bfd6e395acc86c454044c9ee
Compiled from "Test.java"
public class Test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Class              #2            // Test
#2 = Utf8               Test
#3 = Class              #4            // java/lang/Object
#4 = Utf8               java/lang/Object
#5 = Utf8               <init>
#6 = Utf8               ()V
#7 = Utf8               Code
#8 = Methodref          #3.#9         // java/lang/Object."<init>":()V
#9 = NameAndType        #5:#6         // "<init>":()V
#10 = Utf8               LineNumberTable
#11 = Utf8               LocalVariableTable
#12 = Utf8               this
#13 = Utf8               LTest;
#14 = Utf8               test
#15 = Class              #16           // java/lang/String
#16 = Utf8               java/lang/String
#17 = String             #18           // Rafael Nadal
#18 = Utf8               Rafael Nadal
#19 = String             #20           // Novak Djokovic
#20 = Utf8               Novak Djokovic
#21 = String             #22           // Stanislas Wawrinka
#22 = Utf8               Stanislas Wawrinka
#23 = String             #24           // David Ferrer
#24 = Utf8               David Ferrer
#25 = String             #26           // Roger Federer
#26 = Utf8               Roger Federer
#27 = String             #28           // Andy Murray
#28 = Utf8               Andy Murray
#29 = String             #30           // Tomas Berdych
#30 = Utf8               Tomas Berdych
#31 = String             #32           // Juan Martin Del Potro
#32 = Utf8               Juan Martin Del Potro
#33 = Methodref          #34.#36       // java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
#34 = Class              #35           // java/util/Arrays
#35 = Utf8               java/util/Arrays
#36 = NameAndType        #37:#38       // asList:([Ljava/lang/Object;)Ljava/util/List;
#37 = Utf8               asList
#38 = Utf8               ([Ljava/lang/Object;)Ljava/util/List;
#39 = NameAndType        #40:#41       // accept:()Ljava/util/function/Consumer;
#40 = Utf8               accept
#41 = Utf8               ()Ljava/util/function/Consumer;
#42 = InvokeDynamic      #0:#39        // #0:accept:()Ljava/util/function/Consumer;
#43 = InterfaceMethodref #44.#46       // java/util/List.forEach:(Ljava/util/function/Consumer;)V
#44 = Class              #45           // java/util/List
#45 = Utf8               java/util/List
#46 = NameAndType        #47:#48       // forEach:(Ljava/util/function/Consumer;)V
#47 = Utf8               forEach
#48 = Utf8               (Ljava/util/function/Consumer;)V
#49 = Utf8               atp
#50 = Utf8               [Ljava/lang/String;
#51 = Utf8               players
#52 = Utf8               Ljava/util/List;
#53 = Utf8               LocalVariableTypeTable
#54 = Utf8               Ljava/util/List<Ljava/lang/String;>;
#55 = Utf8               main
#56 = Utf8               ([Ljava/lang/String;)V
#57 = Methodref          #1.#9         // Test."<init>":()V
#58 = Methodref          #1.#59        // Test.test:()V
#59 = NameAndType        #14:#6        // test:()V
#60 = Utf8               args
#61 = Utf8               lambda$0
#62 = Utf8               (Ljava/lang/String;)V
#63 = Fieldref           #64.#66       // java/lang/System.out:Ljava/io/PrintStream;
#64 = Class              #65           // java/lang/System
#65 = Utf8               java/lang/System
#66 = NameAndType        #67:#68       // out:Ljava/io/PrintStream;
#67 = Utf8               out
#68 = Utf8               Ljava/io/PrintStream;
#69 = Class              #70           // java/lang/StringBuilder
#70 = Utf8               java/lang/StringBuilder
#71 = Methodref          #15.#72       // java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
#72 = NameAndType        #73:#74       // valueOf:(Ljava/lang/Object;)Ljava/lang/String;
#73 = Utf8               valueOf
#74 = Utf8               (Ljava/lang/Object;)Ljava/lang/String;
#75 = Methodref          #69.#76       // java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
#76 = NameAndType        #5:#62        // "<init>":(Ljava/lang/String;)V
#77 = String             #78           // ;
#78 = Utf8               ;
#79 = Methodref          #69.#80       // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#80 = NameAndType        #81:#82       // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#81 = Utf8               append
#82 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
#83 = Methodref          #69.#84       // java/lang/StringBuilder.toString:()Ljava/lang/String;
#84 = NameAndType        #85:#86       // toString:()Ljava/lang/String;
#85 = Utf8               toString
#86 = Utf8               ()Ljava/lang/String;
#87 = Methodref          #88.#90       // java/io/PrintStream.print:(Ljava/lang/String;)V
#88 = Class              #89           // java/io/PrintStream
#89 = Utf8               java/io/PrintStream
#90 = NameAndType        #91:#62       // print:(Ljava/lang/String;)V
#91 = Utf8               print
#92 = Utf8               player
#93 = Utf8               Ljava/lang/String;
#94 = Utf8               SourceFile
#95 = Utf8               Test.java
#96 = Utf8               BootstrapMethods
#97 = Methodref          #98.#100      // 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;
#98 = Class              #99           // java/lang/invoke/LambdaMetafactory
#99 = Utf8               java/lang/invoke/LambdaMetafactory
#100 = NameAndType        #101:#102     // 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;
#101 = Utf8               metafactory
#102 = 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;
#103 = MethodHandle       #6:#97        // 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;
#104 = Utf8               (Ljava/lang/Object;)V
#105 = MethodType         #104          //  (Ljava/lang/Object;)V
#106 = Methodref          #1.#107       // Test.lambda$0:(Ljava/lang/String;)V
#107 = NameAndType        #61:#62       // lambda$0:(Ljava/lang/String;)V
#108 = MethodHandle       #6:#106       // invokestatic Test.lambda$0:(Ljava/lang/String;)V
#109 = MethodType         #62           //  (Ljava/lang/String;)V
#110 = Utf8               InnerClasses
#111 = Class              #112          // java/lang/invoke/MethodHandles$Lookup
#112 = Utf8               java/lang/invoke/MethodHandles$Lookup
#113 = Class              #114          // java/lang/invoke/MethodHandles
#114 = Utf8               java/lang/invoke/MethodHandles
#115 = Utf8               Lookup
{
public Test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #8                  // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 5: 0
LocalVariableTable:
Start  Length  Slot  Name   Signature
0       5     0  this   LTest;

public void test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=4, locals=3, args_size=1
0: bipush        8
2: anewarray     #15                 // class java/lang/String
5: dup
6: iconst_0
7: ldc           #17                 // String Rafael Nadal
9: aastore
10: dup
11: iconst_1
12: ldc           #19                 // String Novak Djokovic
14: aastore
15: dup
16: iconst_2
17: ldc           #21                 // String Stanislas Wawrinka
19: aastore
20: dup
21: iconst_3
22: ldc           #23                 // String David Ferrer
24: aastore
25: dup
26: iconst_4
27: ldc           #25                 // String Roger Federer
29: aastore
30: dup
31: iconst_5
32: ldc           #27                 // String Andy Murray
34: aastore
35: dup
36: bipush        6
38: ldc           #29                 // String Tomas Berdych
40: aastore
41: dup
42: bipush        7
44: ldc           #31                 // String Juan Martin Del Potro
46: aastore
47: astore_1
48: aload_1
49: invokestatic  #33                 // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
52: astore_2
53: aload_2
54: invokedynamic #42,  0             // InvokeDynamic #0:accept:()Ljava/util/function/Consumer;
59: invokeinterface #43,  2           // InterfaceMethod java/util/List.forEach:(Ljava/util/function/Consumer;)V
64: return
LineNumberTable:
line 8: 0
line 9: 17
line 10: 22
line 11: 32
line 12: 44
line 8: 47
line 13: 48
line 14: 53
line 15: 64
LocalVariableTable:
Start  Length  Slot  Name   Signature
0      65     0  this   LTest;
48      17     1   atp   [Ljava/lang/String;
53      12     2 players   Ljava/util/List;
LocalVariableTypeTable:
Start  Length  Slot  Name   Signature
53      12     2 players   Ljava/util/List<Ljava/lang/String;>;

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: new           #1                  // class Test
3: dup
4: invokespecial #57                 // Method "<init>":()V
7: invokevirtual #58                 // Method test:()V
10: return
LineNumberTable:
line 18: 0
line 19: 10
LocalVariableTable:
Start  Length  Slot  Name   Signature
0      11     0  args   [Ljava/lang/String;

private static void lambda$0(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=4, locals=1, args_size=1
0: getstatic     #63                 // Field java/lang/System.out:Ljava/io/PrintStream;
3: new           #69                 // class java/lang/StringBuilder
6: dup
7: aload_0
8: invokestatic  #71                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
11: invokespecial #75                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
14: ldc           #77                 // String ;
16: invokevirtual #79                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #83                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: invokevirtual #87                 // Method java/io/PrintStream.print:(Ljava/lang/String;)V
25: return
LineNumberTable:
line 14: 0
LocalVariableTable:
Start  Length  Slot  Name   Signature
0      26     0 player   Ljava/lang/String;
}
SourceFile: "Test.java"
BootstrapMethods:
0: #103 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:
#105 (Ljava/lang/Object;)V
#108 invokestatic Test.lambda$0:(Ljava/lang/String;)V
#109 (Ljava/lang/String;)V
InnerClasses:
public static final #115= #111 of #113; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles


可以看到Java使用了dymanic invoke ( 54: invokedynamic #42, 0 // InvokeDynamic #0:accept:()Ljava/util/function/Consumer; –> #42 = InvokeDynamic #0:#39 –> 0: #103 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandlesLookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;)调用[BootstrapMethods](https://docs.oracle.com/javase/8/docs/technotes/guides/vm/multiple-language-support.html#defining_bootstrap) ,从bootstrap的实现我们可以知道它的内部实现是调用了java/lang/invoke/LambdaMetafactory的[metafactory](https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/LambdaMetafactory.html#metafactory-java.lang.invoke.MethodHandles.Lookup-java.lang.String-java.lang.invoke.MethodType-java.lang.invoke.MethodType-java.lang.invoke.MethodHandle-java.lang.invoke.MethodType-)。前面3个参数由JVM自动入栈提供,后面3个参数分别是参数以及返回类型( #105 MethodType (Ljava/lang/Object;)V),被调用的lambda函数( #108 MethodHandle invokestatic Test.lambdaLookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;)调用[BootstrapMethods](https://docs.oracle.com/javase/8/docs/technotes/guides/vm/multiple-language-support.html#defining_bootstrap) ,从bootstrap的实现我们可以知道它的内部实现是调用了java/lang/invoke/LambdaMetafactory的[metafactory](https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/LambdaMetafactory.html#metafactory-java.lang.invoke.MethodHandles.Lookup-java.lang.String-java.lang.invoke.MethodType-java.lang.invoke.MethodType-java.lang.invoke.MethodHandle-java.lang.invoke.MethodType-)。前面3个参数由JVM自动入栈提供,后面3个参数分别是参数以及返回类型( #105 MethodType (Ljava/lang/Object;)V),被调用的lambda函数( #108 MethodHandle invokestatic Test.lambda0:(Ljava/lang/String;)V)以及参数以及参数以及返回实际类型(#109 MethodType (Ljava/lang/String;)V)。对我们这个例子来说就是调用了 private static void lambda0(java.lang.String)。再看lambda0(java.lang.String)。再看lambda0的实现,以String player(LocalVariableTable)为参数,打印输出。那么循环在哪里实现了呢?这就是invokedymanic实现的精髓所在,不在编译阶段实现,在运行阶段动态生成实现。为什么要做成这样的实现呢?具体可以参考JSR335,大神们解释了原因,考虑的主要是先把lambda表达式转换成static method,然后把调用lambda表达式的使用转化成动态调用,实际的实现可能包含内部类(public static final #115= #111 of #113;)或者dymanic proxy或者method handles等等,为了找出哪种实现策略最优,在第一次调用的时候选择最优绑定策略,后续自动使用最优策略,在第一次使用的时候时间有所消耗,后续就不再有消耗,非常简单有效的典型Java策略。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: