Java8 lambda表达式的实现探索
2016-01-06 16:16
465 查看
Java8引入了大家千呼万唤的lambda表达式的实现,作为码农,比较好奇的就是如何用以及如何实现的。
如何用就不用多说了,大把地方告诉你怎么用。底层如何实现的就比较有意思了。对我来说要实现lambda表达式,第一反应就是把lambda表达式转化成Java已有的实现,比如内部类,比如动态代理。那么接下来我们看看Java8里面的实现,给出个测试类
对应的字节码
可以看到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策略。
如何用就不用多说了,大把地方告诉你怎么用。底层如何实现的就比较有意思了。对我来说要实现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策略。
相关文章推荐
- Java Script 第十一节课 Java Script的中的函数-通过function关键
- 学习资料网址
- (Java基础--反射)理解反射的概念
- java 的synchronized 机制详细介绍
- eclipse安装maven插件时出错
- Java常用工具类(一)
- Java单例模式探讨
- Eclipse中CVS界面功能描述
- eclipse 安装 Fatjar 报错Failed to prepare partial IU
- Think in Java 笔记(2)
- Think in Java 笔记
- Java中PrintStream(打印输出流)
- Java 将数字123对应于中文的一二三
- 原来Java中有两个ArrayList
- java基础————java高新技术之反射
- java.util.vector中的vector的详细用法
- Soot学习笔记3.使用soot解析java文件
- (Java基础--反射)透彻分析反射的基础--Class类
- Java 编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串
- 关于JAVA中构造函数的问题