Java8 увеличилась лямбда-выражений, в значительной степени, так что код становится более простым и компактным, то Java8 лямбда-выражения, как достичь его?
Прямой взгляд на простом примере создания потока.
public class TestLambda {
public static void main(String[] args) {
new Thread(() -> System.out.print("thread"));
}
}
Выполнить javac TestLambda.java
скомпилированный файл TestLambda.class
, а затем использовать javap
команду для разбора файлов классов.
Выполнение javap -p TestLambda
показывает все классы и член.
// javap -p TestLambda
Compiled from "TestLambda.java"
public class TestLambda {
public TestLambda();
public static void main(java.lang.String[]);
private static void lambda$main$0(); // 编译后生成的
}
Как видно из приведенного выше кода, компилятор генерирует частные статический метод лямбда-выражения.
private static void lambda$main$0();
Используйте команду javap -v -p TestLambda
для печати подробной информации.
public class TestLambda
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #9.#24 // java/lang/Object."<init>":()V
#2 = Class #25 // java/lang/Thread
#3 = InvokeDynamic #0:#30 // #0:run:()Ljava/lang/Runnable;
#4 = Methodref #2.#31 // java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
#5 = Fieldref #32.#33 // java/lang/System.out:Ljava/io/PrintStream;
#6 = String #34 // thread
#7 = Methodref #35.#36 // java/io/PrintStream.print:(Ljava/lang/String;)V
#8 = Class #37 // TestLambda
#9 = Class #38 // java/lang/Object
#10 = Utf8 <init>
#11 = Utf8 ()V
#12 = Utf8 Code
#13 = Utf8 LineNumberTable
#14 = Utf8 LocalVariableTable
#15 = Utf8 this
#16 = Utf8 LTestLambda;
#17 = Utf8 main
#18 = Utf8 ([Ljava/lang/String;)V
#19 = Utf8 args
#20 = Utf8 [Ljava/lang/String;
#21 = Utf8 lambda$main$0
#22 = Utf8 SourceFile
#23 = Utf8 TestLambda.java
#24 = NameAndType #10:#11 // "<init>":()V
#25 = Utf8 java/lang/Thread
#26 = Utf8 BootstrapMethods
#27 = MethodHandle #6:#39 // 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;
#28 = MethodType #11 // ()V
#29 = MethodHandle #6:#40 // invokestatic TestLambda.lambda$main$0:()V
#30 = NameAndType #41:#42 // run:()Ljava/lang/Runnable;
#31 = NameAndType #10:#43 // "<init>":(Ljava/lang/Runnable;)V
#32 = Class #44 // java/lang/System
#33 = NameAndType #45:#46 // out:Ljava/io/PrintStream;
#34 = Utf8 thread
#35 = Class #47 // java/io/PrintStream
#36 = NameAndType #48:#49 // print:(Ljava/lang/String;)V
#37 = Utf8 TestLambda
#38 = Utf8 java/lang/Object
#39 = Methodref #50.#51 // 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;
#40 = Methodref #8.#52 // TestLambda.lambda$main$0:()V
#41 = Utf8 run
#42 = Utf8 ()Ljava/lang/Runnable;
#43 = Utf8 (Ljava/lang/Runnable;)V
#44 = Utf8 java/lang/System
#45 = Utf8 out
#46 = Utf8 Ljava/io/PrintStream;
#47 = Utf8 java/io/PrintStream
#48 = Utf8 print
#49 = Utf8 (Ljava/lang/String;)V
#50 = Class #53 // java/lang/invoke/LambdaMetafactory
#51 = NameAndType #54:#58 // 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;
#52 = NameAndType #21:#11 // lambda$main$0:()V
#53 = Utf8 java/lang/invoke/LambdaMetafactory
#54 = Utf8 metafactory
#55 = Class #60 // java/lang/invoke/MethodHandles$Lookup
#56 = Utf8 Lookup
#57 = Utf8 InnerClasses
#58 = 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;
#59 = Class #61 // java/lang/invoke/MethodHandles
#60 = Utf8 java/lang/invoke/MethodHandles$Lookup
#61 = Utf8 java/lang/invoke/MethodHandles
{
public TestLambda();
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 1: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LTestLambda;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
0: new #2 // class java/lang/Thread
3: dup
4: invokedynamic #3, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
9: invokespecial #4 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
12: pop
13: return
LineNumberTable:
line 3: 0
line 4: 13
LocalVariableTable:
Start Length Slot Name Signature
0 14 0 args [Ljava/lang/String;
private static void lambda$main$0();
descriptor: ()V
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=2, locals=0, args_size=0
0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #6 // String thread
5: invokevirtual #7 // Method java/io/PrintStream.print:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 3: 0
}
SourceFile: "TestLambda.java"
InnerClasses:
public static final #56= #55 of #59; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
0: #27 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:
#28 ()V
#29 invokestatic TestLambda.lambda$main$0:()V
#28 ()V
Показанный выше: основной () метод, чтобы создать поток строки кода байт-код будет декомпилированы
0: new #2 // class java/lang/Thread
3: dup
4: invokedynamic #3, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
9: invokespecial #4 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
12: pop
13: return
По 4: invokedynamic #3
местонахождению третьей линии постоянного бассейна
#3 = InvokeDynamic #0:#30 // #0:run:()Ljava/lang/Runnable;
Среди них, #0
указывая на BootstrapMethods:
статический метод фабрикиLambdaMetafactory.metafactory
BootstrapMethods:
0: #27 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:
#28 ()V
#29 invokestatic TestLambda.lambda$main$0:()V
#28 ()V
LambdaMetafactory.metafactory
Asm будет использовать внутренний класс может генерировать лямбда-выражения, metafactory метод в качестве источника
public static CallSite metafactory(MethodHandles.Lookup caller,
String invokedName,
MethodType invokedType,
MethodType samMethodType,
MethodHandle implMethod,
MethodType instantiatedMethodType)
throws LambdaConversionException {
AbstractValidatingLambdaMetafactory mf;
mf = new InnerClassLambdaMetafactory(caller, invokedType,
invokedName, samMethodType,
implMethod, instantiatedMethodType,
false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
mf.validateMetafactoryArgs();
return mf.buildCallSite();
}
Чтобы увидеть этот внутренний класс, с параметрами генерируется прокси - класс запустить TestLambda, то есть java -Djdk.internal.lambda.dumpProxyClasses TestLambda
. После окончания бега, он находится в том же каталоге , чтобы сгенерировать файл класса TestLambda$$Lambda$1.class
. Исполнение javap -v -p TestLambda$$Lambda$1
декомпилирует класс.
final class TestLambda$$Lambda$1 implements java.lang.Runnable
minor version: 0
major version: 52
flags: ACC_FINAL, ACC_SUPER, ACC_SYNTHETIC
Constant pool:
#1 = Utf8 TestLambda$$Lambda$1
#2 = Class #1 // TestLambda$$Lambda$1
#3 = Utf8 java/lang/Object
#4 = Class #3 // java/lang/Object
#5 = Utf8 java/lang/Runnable
#6 = Class #5 // java/lang/Runnable
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = NameAndType #7:#8 // "<init>":()V
#10 = Methodref #4.#9 // java/lang/Object."<init>":()V
#11 = Utf8 run
#12 = Utf8 Ljava/lang/invoke/LambdaForm$Hidden;
#13 = Utf8 TestLambda
#14 = Class #13 // TestLambda
#15 = Utf8 lambda$main$0
#16 = NameAndType #15:#8 // lambda$main$0:()V
#17 = Methodref #14.#16 // TestLambda.lambda$main$0:()V
#18 = Utf8 Code
#19 = Utf8 RuntimeVisibleAnnotations
{
private TestLambda$$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 void run();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=0, locals=1, args_size=1
0: invokestatic #17 // Method TestLambda.lambda$main$0:()V
3: return
RuntimeVisibleAnnotations:
0: #12()
}
Как показано выше, внутренний класс TestLambda$$Lambda$1
реализует java.lang.Runnable, run()
метод , который метод выполнения после запуска нити, это вызовет выполнение TestLambda.lambda$main$0:()
, что компилирует TestLambda
частный статический метод генерируется , когда.
резюме
Лямбда-выражение, созданное поток будет добавить закрытый статический метод в классе файлах скомпилирован, запущенных в этом классе, будет использовать технологические операции ASM байт-код динамически создает внутренний класс, этот внутренний класс реализует Runnable интерфейса, реальные поток выполнения, способ запуска нити на самом деле внутри частного статического вызова метода, генерируемого компилятором для выполнения многопоточного кода.