lambda原理

当我们编写

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;
    }

}


方法引用原理、闭包原理与这个类似。需要的话私信我,后面我会出相关的文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值