Lambda原理及应用
Lambda介绍
Lambda 是 JDK8 以后版本推出的一个新特性,也是一个重要的版本更新,利用 Lambda 可以简化内部类,可以更方便的进行集合的运算,让你的代码看起来更加简洁,也能提升代码的运行效率。
Lambda语法
非静态类
(parameters...) -> expression
或者
(parameters...) ->{ statements; }
静态类
(parameters...) -> Class.Method(parameters...)
通常也可以简写成
Class::Method
举个例子
() -> 1
i -> 2 * i
(String s) -> {System.out.print(s);}
(o1, o2) -> Integer.compare(o1, o2)
Integer::compare
Lambda的原理
例子
我们来看下面的例子,使用 Lambda 实现一个 Runable 线程。
package org.lin;
public class LambdaTest {
public static void main(String[] args) {
new Thread(() -> System.out.println("run")).start();
}
}
分析一下它的字节码:
// access flags 0x21
public class org/lin/LambdaTest {
// compiled from: LambdaTest.java
// access flags 0x19
public final static INNERCLASS java/lang/invoke/MethodHandles$Lookup java/lang/invoke/MethodHandles Lookup
// access flags 0x1
public <init>()V
L0
LINENUMBER 3 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this Lorg/lin/LambdaTest; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x9
public static main([Ljava/lang/String;)V
L0
LINENUMBER 6 L0
NEW java/lang/Thread
DUP
INVOKEDYNAMIC run()Ljava/lang/Runnable; [
// handle kind 0x6 : 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;
// arguments:
()V,
// handle kind 0x6 : INVOKESTATIC
org/lin/LambdaTest.lambda$main$0()V,
()V
]
INVOKESPECIAL java/lang/Thread.<init> (Ljava/lang/Runnable;)V
INVOKEVIRTUAL java/lang/Thread.start ()V
L1
LINENUMBER 7 L1
RETURN
L2
LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
MAXSTACK = 3
MAXLOCALS = 1
// access flags 0x100A
private static synthetic lambda$main$0()V
L0
LINENUMBER 6 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "run"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
RETURN
MAXSTACK = 2
MAXLOCALS = 0
}
关键指令
INVOKEDYNAMIC run()Ljava/lang/Runnable; [
// handle kind 0x6 : 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;
// arguments:
()V,
// handle kind 0x6 : INVOKESTATIC
org/lin/LambdaTest.lambda$main$0()V,
()V
]
通过调用一个 INVOKEDYNAMIC 指令 ,将方法链接到这个方法上 org/lin/LambdaTest.lambda$main$0()V 。
这个方法的命名方式 ,很可能 JDK 内部帮我们生成了一个内部类, org/lin/LambdaTest.lambda$main
通过 jclasslib 工具查看,验证了我们的想法:
Invokedynamic 和 LambdaMetafactory
Invokedynamic
Invokedynamic 是 JDK7 引入的一条新指令,通过一个调用点 CallSite 和 方法句柄 MethodHandle,来完成一个方法的调用。
CallSite 就是一个 MethodHandle 的 Holder,MethodHandle 指向一个调用点真正执行的方法。
LambdaMetafactory
LambdaMetafactory 是 JDK 内部 CallSite 和 MethodHandle 绑定实现类。
LambdaMetafactory的关键方法
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();
}
- MethodHandles.Lookup caller – 调用者,也就是 Invokedynamic 指令运行的上下文,通常可以通过 Lookup#lookupClass() 获取。
- String invokedName – Lambda 实现的接口的方法名称。
- MethodType invokedType – 调用点的方法签名描述 ,
- MethodType samMethodType – Lambda 实现的接口方法的签名描述 。(sam 就 single public abstract method 的缩写)
- MethodHandle implMethod – 调用点的方法句柄。
- MethodType instantiatedMethodType – 运行时调用点的方法签名描述。
参考:
https://zhuanlan.zhihu.com/p/28124632
https://www.jianshu.com/p/d74e92f93752
https://www.jianshu.com/p/7ef49246c176
https://www.cnblogs.com/wzqshb/p/16987548.html