当我们编写
BinaryOperator<Integer> lambda = (a, b) -> a + b;的时候,底层是怎么实现的呢?
其实就是根据我们的逻辑生成一个静态方法
然后创建一个类引用接口(如BinaryOperator),实现里面的抽象方法,具体逻辑就是调用生成的静态方法。
1、生成静态方法(lambda的逻辑)
如果我们反编译生成的字节码文件,我们会发现jvm帮我们生成了一个静态方法
lambda$main$0
这个方法实现的逻辑和(a, b) -> a + b;是一样的。参数一致、返回值一致
2、MethodHandle
作用是底层帮我们创建函数对象,这个 在第三点会提到
作用和反射类似
查找类的信息和方法,由于反射的api的性能问题和安全问题,lambda并没有使用反射,而是使用MethodHandle
下面是MethodHandle三个常见的方法,都需要先获取到lookup对象才能调用
MethodHandle 铺垫
MethodHandle 的执行权限与上下文相关
原本有权限调用的方法,正常能调用,通过 MethodHandle 反射也能调用
原本没权限调用的方法,正常不能调用,MethodHandle 反射也调用不了
a) 反射调用静态方法 findStatic
MethodHandle mh = MethodHandles.lookup().findStatic(C01Lambda1.class, "lambda$main$2",
MethodType.methodType(Integer.class, Integer.class, Integer.class));
System.out.println(mh.invoke(1, 2)); // 相当于 C01Lambda1.lambda$main$2(1,2)
b) 反射调用非静态方法 findVirtual
MethodHandle mh2 = MethodHandles.lookup().findVirtual(MyLambda.class, "apply",
MethodType.methodType(Integer.class, Integer.class, Integer.class));
System.out.println(mh2.invoke(new MyLambda(), 3, 4)); // 相当于通过 new MyLambda() 对象执行了 .apply(3,4),记得非静态方法第一个参数要传一个对象过去,就是调用哪个对象的方法
c) 反射调用构造 findConstructor
MethodHandle mh3 = MethodHandles.lookup().findConstructor(MyLambda.class, MethodType.methodType(void.class));
System.out.println(mh3.invoke()); // 相当于 new MyLambda()
3、生成类和对象(实现步骤)--关键
生成函数对象所需的类,就是根据我们写的lambda表达式,jvm帮我们生成函数对象
底层就是创建了一个类,实现了抽象接口。
然后通过下面的步骤去调用里面的方法 使用MethodHandle就能调用对应的方法
所需要的参数,我们都传给底层了
步骤:
1、CallSite cs = LambdaMetafactory.metafactory() 创建了一个元方法工厂 2、MethodHandle mh = cs.getTarget(); // 获取一个方法句柄(MethodHandle),它指向一个目标方法 3、BinaryOperator<Integer> invoke = (BinaryOperator<Integer>) mh.invoke();//调用工厂方法 4、invoke.apply(5, 6)//方法调用
LambdaMetafactory.metafactory的参数
// 1. lookup // 2. 接口方法名 // 3. 创建函数对象工厂方法长相 BinaryOperator factory() // 4. 接口方法长相 // 5. 实现方法 (本例就是下面的静态方法 lambda$main$2) // 6. 函数对象实际长相
CallSite cs = LambdaMetafactory.metafactory(
lookup, // MethodHandles.Lookup: 表示当前类的上下文,用于访问类的方法和字段
"apply", // String: 被调用的方法名(函数式接口的抽象方法名,比如 "apply")
MethodType.methodType(BinaryOperator.class), // MethodType: 描述调用点的方法签名,表示 lambda 表达式的类型 (BinaryOperator<Integer>)
MethodType.methodType(Integer.class, Integer.class, Integer.class), // MethodType: SAM 方法的签名,表示函数式接口中的抽象方法类型(比如 BinaryOperator<Integer>.apply 接受两个 Integer 并返回一个 Integer)
MethodHandles.lookup().findStatic(MyClass.class, "lambdaImpl", MethodType.methodType(Integer.class, Integer.class, Integer.class)), // MethodHandle: 指向实际的实现方法,比如 MyClass 中的静态方法 lambdaImpl
MethodType.methodType(Integer.class, Integer.class, Integer.class) // MethodType: 实例化的方法签名,指定具体的返回类型和参数类型(与 SAM 方法签名相同)
);
// -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles
public class C01Lambda1 {
public static void main(String[] args) throws Throwable {
BinaryOperator<Integer> lambda = (a, b) -> a + b;
System.in.read();
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle impl = lookup.findStatic(C01Lambda1.class, "lambda$main$2",
MethodType.methodType(Integer.class, Integer.class, Integer.class));
// 内部:生成函数对象所需的类 ASM
CallSite cs = LambdaMetafactory.metafactory(
lookup, // 1. lookup
"apply", // 2. 接口方法名
MethodType.methodType(BinaryOperator.class), // 3. 创建函数对象工厂方法长相 BinaryOperator factory()
MethodType.methodType(Object.class, Object.class, Object.class), // 4. 接口方法长相(接口使用的是泛型,直接给Object就行)
impl, // 5. 实现方法 (本例就是下面的静态方法 lambda$main$2)
MethodType.methodType(Integer.class, Integer.class, Integer.class) // 6. 函数对象实际长相
);
// BinaryOperator factory() {return new MyLambda()}
MethodHandle mh = cs.getTarget(); // 就是函数对象工厂方法(产生对象)
BinaryOperator<Integer> invoke = (BinaryOperator<Integer>) mh.invoke();//返回的结果就是函数对象,就是上面的(a, b) -> a + b;
System.out.println(invoke.apply(5, 6));
}
static final class MyLambda implements BinaryOperator<Integer> {
private MyLambda() {}
@Override
public Integer apply(Integer a, Integer b) {
return lambda$main$2(a, b);
}
}
private static Integer lambda$main$2(Integer a, Integer b) {
return a + b;
}
}
方法引用原理、闭包原理与这个类似。需要的话私信我,后面我会出相关的文章