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.lambda 0:(Ljava/lang/String;)V)以及參數以及參數以及返回實際類型(#109 MethodType (Ljava/lang/String;)V)。對我們這個例子來說就是調用了 private static void lambda