lambda表达式原理

当前java版本为java17

lambda经典写法

先看几个lambda的定义

/// 匿名函数式lambda表达式
Runnable r = () -> {
   };
Function<Integer, Integer> func = x -> x + 1;

// 静态方法引用式lambda表达式
BiFunction<HashSet<String>, String, Boolean> func2 = HashSet::add;

Set<String> set = new HashSet<>();
// 实例方法引用式lambda表达式
Function<String, Boolean> func3 = set::add;

// 静态方法引用式lambda表达式; 这里调用的方法直接就是静态方法
Function<Object, Object> f = Objects::requireNonNull;

// lambda的优化
Function<Object, Object> func = Objects::requireNonNull;
Consumer<String> optimizeFunc = Objects::requireNonNull;


// 构造器引用式lambda表达式
// 无参构造
Supplier<List<Integer>> func4 = ArrayList::new;
// 有参构造ArrayList(size)
Function<Integer, List<Integer>> func5 = ArrayList::new;

// lambada表达式实现接口
Function<Student, String> func = (Function<Student, String> & Serializable) Student::getName;

lambda原理探究

说明: 跟着代码debug的时候不要使用@Test注解的方法去跟, 因为它也用了动态语言脚本技术, 直接用main方法测试即可

Runnable r = () -> {}

public class LambdaView {
   
	public static void main(String[] args) {
   
        // 函数式接口
        Runnable r = () -> {
   };
        System.out.println("lambda类的简单类名:" + r.getClass().getSimpleName());
        System.out.println("lambda类的名字:" + r.getClass().getName());
        System.out.println("lambda类是否为匿名类:" + r.getClass().isAnonymousClass());    // false
        // 也可以用 r.getClass().getEnclosingClass() != null 判断是否有外围类
        System.out.println("lambda类是否为内部类:" + r.getClass().isMemberClass());    // false
        System.out.println("lambda类是否为本地类(局部类):" + r.getClass().isLocalClass());    // false
        System.out.println("lambda类是否为隐藏类:" + r.getClass().isHidden());    // true
        System.out.println("lambda类是否为动态生成的类:" + r.getClass().isSynthetic());    // true

        Runnable r2 = new Runnable() {
   
            @Override
            public void run() {
   

            }
        };
        System.out.println("匿名内部类的简单类名:" + r2.getClass().getSimpleName());
        System.out.println("匿名内部类的名字:" + r2.getClass().getName());
        System.out.println(r2.getClass().isAnonymousClass());   // true

        MyRunnable r3 = new MyRunnable();
        System.out.println("常规内部类的简单类名:" + r3.getClass().getSimpleName());
        System.out.println(r3.getClass().getName());
        System.out.println(r3.getClass().isAnonymousClass());   // false
	}	
	static class MyRunnable implements Runnable {
   
        @Override
        public void run() {
   

        }
    }
}

输出结果

lambda类的简单类名:LambdaView$$Lambda$14/0x0000000801001208
lambda类的名字:per.qiao.myutils.lambda.LambdaView$$Lambda$14/0x0000000801001208
lambda类是否为匿名类:false
lambda类是否为内部类:false
lambda类是否为本地类(局部类):false
lambda类是否为隐藏类:true
lambda类是否为动态生成的类:true
匿名内部类的简单类名:
匿名内部类的名字:per.qiao.myutils.lambda.LambdaView$1
true
常规内部类的简单类名:MyRunnable
per.qiao.myutils.lambda.LambdaView$MyRunnable
false

我们看到lambda表达式的Runnable r的类名包含了$$Lambda$, 它既不是内部类也不是本地类也不是匿名类, 而是一个隐藏类,一个动态生成的类

接下来我们单独研究这个r

public class LambdaView {
   

    public static void main(String[] args) {
   
        // 函数式接口
        Runnable r = () -> {
   };
    }
}

使用javap -c查看class文件的字节码

javap -c LambdaView.class

结果如下

public class per.qiao.myutils.lambda.LambdaView {
   
  public per.qiao.myutils.lambda.LambdaView();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: invokedynamic #7,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
       5: astore_1
       6: return
}

第一个code部分是LambdaView类的无参构造部分, 我们重点关注第二个code部分

其中有个invokedynamic指令, 它是java7中引入的一种动态方法调用指令, 主要是为了支持动态类型语言和脚本语言在JVM上的高效实现。

与传统的 invokevirtual 或 invokestatic 不同,invokedynamic 不直接绑定到特定方法,而是使用引导方法 (bootstrap method) 动态决定调用目标。

Java 中的 lambda 表达式,引导方法通常是 java.lang.invoke.LambdaMetafactory.metafactory 方法。

这里的代码Runnable r = () -> {}会调用到LambdaMetafactory#metafactory方法

调用链路: JVM => MethodHandleNatives#linkCallSite =>CallSite#makeSite=>BootstrapMethodInvoker.invoke

LambdaMetafactory#metafactory

/**
     * 此方法用于动态生成 lambda 表达式实现,并返回一个 CallSite,用于实际调用 lambda 实现的目标。
     * 
     * @param caller    用于查找类和方法的上下文
     * @param interfaceMethodName   要实现的接口方法名
     * @param factoryType   返回lambda工厂的类型
     * @param interfaceMethodType   接口方法的签名
     * @param implementation    目标实现的句柄
     * @param dynamicMethodType 实际调用的动态方法签名
     * @return
     * @throws LambdaConversionException
     */
public static CallSite metafactory(MethodHandles.Lookup caller,
                                       String interfaceMethodName,
                                       MethodType factoryType,
                                       MethodType interfaceMethodType,
                                       MethodHandle implementation,
                                       MethodType dynamicMethodType)
            throws LambdaConversionException {
   
    AbstractValidatingLambdaMetafactory mf;
    mf = new InnerClassLambdaMetafactory(Objects.requireNonNull(caller),
                                         Objects.requireNonNull(factoryType),
                                         Objects.requireNonNull(interfaceMethodName),
                                         Objects.requireNonNull(interfaceMethodType),
                                         Objects.requireNonNull(implementation),
                                         Objects.requireNonNull(dynamicMethodType),
                                         false,
                                         EMPTY_CLASS_ARRAY,
                                         EMPTY_MT_ARRAY);
    mf.validateMetafactoryArgs();
    return mf.buildCallSite();
}

debug看一下各个参数的值

caller = per.qiao.myutils.lambda.LambdaView
interfaceMethodName=run
factoryType=()Runnable
interfaceMethodType=()void
implementation=MethodHandle()void
dynamicMethodType=()void

这里我们关注一下buildCallSite方法

@Override
CallSite buildCallSite() throws LambdaConversionException {
   
    final Class<?> innerClass = spinInnerClass();
	// .... 省略代码
	try {
   
        Object inst = ctrs[0].newInstance();
        return new ConstantCallSite(MethodHandles.constant(interfaceClass, inst));
    }
}

private Class<?> spinInnerClass() throws
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

uncleqiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值