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表达式首次调用的时候,进行转换和链接;之后的调用都会跳过这一步骤