Java8 lambda表达式的实现探索

来源:互联网 发布:北京淘宝摄影 编辑:程序博客网 时间:2024/04/29 04:32

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.classClassfile /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_SUPERConstant 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;)VInnerClasses:     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.lambda0:(Ljava/lang/String;)V)以及参数以及参数以及返回实际类型(#109 MethodType (Ljava/lang/String;)V)。对我们这个例子来说就是调用了 private static void 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策略。

0 0
原创粉丝点击