work hard work smart

专注于Java后端开发。 不断总结,举一反三。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Java 8 Lambda表达式实现原理解析

Posted on 2020-04-08 15:03  work hard work smart  阅读(2309)  评论(0编辑  收藏  举报

Lambda原理

在Java8中每一个表达式必须有一个函数式接口与之对应。

 

什么函数式接口?

简单的说就是只包含一个抽象方法的普通接口

 

Lambda表达式的使用

我们定义了一个IMath接口,加上@FunctionalInterface注解

public class LambdaTest {

    @FunctionalInterface
    interface IMath{
        int operation(int a, int b);
    }
    

    int testLambda(IMath lambdaTest, int a , int b) {
      return lambdaTest.operation(a,b);
    }

    public static void main(String[] args) {
        LambdaTest lambdaTest = new LambdaTest();

        int result = lambdaTest.testLambda(
                (a,b) -> a + b
                , 1, 2);
        System.out.println(result);
    }
}

  然后定义一个testLambda方法,最终在main方法中使用lambda表达式。

 

1、使用字节码查看工具 javap -p LambdaTest.class

-p表示输出所有的类及成员

Compiled from "LambdaTest.java"
public class java8.LambdaTest {
  public java8.LambdaTest();
  int testLambda(java8.LambdaTest$IMath, int, int);
  public static void main(java.lang.String[]);
  private static int lambda$main$0(int, int);
}

  可以看到生成了一个源码没有的私有的静态函数private static int lambda$main$0(int, int);

如何实现这个私有静态方法呢?我们在LambdaMetafactory类中的metafactory方法上打上断点

 

 最终进入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();
    }

  这个函数的作用: 为Lambda表达式生成一个内部类。这个类是怎么样的?

 

2、查看metafactory函数生成的内部类

在Idea 的VM options配置-Djdk.internal.lambda.dumpProxyClasses

 

 运行后,会将生成的内部类class输出到一个文件中

package java8;

import java.lang.invoke.LambdaForm.Hidden;
import java8.LambdaTest.IMath;

// $FF: synthetic class
final class LambdaTest$$Lambda$1 implements IMath {
    private LambdaTest$$Lambda$1() {
    }

    @Hidden
    public int operation(int var1, int var2) {
        return LambdaTest.lambda$main$0(var1, var2);
    }
}

  最终调用的是LambdaTest的内部私有静态方法 lambda$main$0(var1, var2);

 总结:Lambda并不是采用内部类的实现方式实现的。如果Lambda表达式使用内部类的方式,将是极为不利的。类加载需要有加载、验证、准备、解析、初始化等过程,大量的内部类将会影响应用执行的性能,并消耗Metaspace。 Lambda表达式首次调用的时候,进行转换和链接;之后的调用都会跳过这一步骤