为了支持函数式编程,Java 8引入了Lambda表达式,那么在Java 8中到底是如何实现Lambda表达式的呢? Lambda表达式经过编译之后,到底会生成什么东西呢? 在没有深入分析前,让我们先想一想,Java 8中每一个Lambda表达式必须有一个函数式接口与之对应,函数式接口与普通接口的区别,可以参考前面的内容,那么你或许在想Lambda表达式是不是转化成与之对应的函数式接口的一个实现类呢,然后通过多态的方式调用子类的实现呢,如下面代码是一个Lambda表达式的样例

@FunctionalInterface
interface Print<T> {
public void print(T x);
}
public class Lambda {
public static void PrintString(String s, Print<String> print) {
print.print(s);
}
public static void main(String[] args) {
PrintString("test", (x) -> System.out.println(x));
}
}

按照上面的分析,理论上经过编译器处理后,最终生成的代码应该如下面所示:

@FunctionalInterface
interface Print<T> {
public void print(T x);
} class Lambda$$0 implements Print<String> {
@Override
public void print(String x) {
System.out.println(x);
}
} public class Lambda {
public static void PrintString(String s,
Print<String> print) {
print.print(s);
}
public static void main(String[] args) {
PrintString("test", new Lambda$$0());
}
}

再或者是一个内部类实现,代码如下所示:

@FunctionalInterface
interface Print<T> {
public void print(T x);
}
public class Lambda {
final class Lambda$$0 implements Print<String> {
@Override
public void print(String x) {
System.out.println(x);
}
}
public static void PrintString(String s,
Print<String> print) {
print.print(s);
}
public static void main(String[] args) {
PrintString("test", new Lambda().new Lambda$$0());
}
}

异或是这种匿名内部类实现,代码如下所示:

@FunctionalInterface
interface Print<T> {
public void print(T x);
}
public class Lambda {
public static void PrintString(String s,
Print<String> print) {
print.print(s);
}
public static void main(String[] args) {
PrintString("test", new Print<String>() {
@Override
public void print(String x) {
System.out.println(x);
}
});
}
}

上面的代码,除了在代码长度上长了点外,与用Lambda表达式实现的代码运行结果是一样的,那么Java 8到底是用什么方式实现的呢? 是不是上面三种实现方式中的一种呢,你也许觉的自已想的是对的,其实本来也就是对的,在Java 8中采用的是内部类来实现Lambda表达式

那么Lambda表达式到底是如何实现的呢?

为了探究Lambda表达式是如何实现的,就得需要研究Lambda表过式最终转化成的字节码文件,这就需要jdk的bin目录下的一个字节码查看工具及反编译工具

javap -p Lambda.class

上面命令中的-p表示输出所有类及成员,运行上面的命令后,得的结果如下所示:

Compiled from "Lambda.java"
public class Lambda {
public Lambda();
public static void PrintString(java.lang.String, Print<java.lang.String>);
public static void main(java.lang.String[]);
private static void lambda$0(java.lang.String);
}

由上面的代码可以看出编译器会根据Lambda表达式生成一个私有的静态函数,注意,在这里说的是生成,而不是等价

private static void lambda$0(java.lang.String);

为了验证上面的转化是否正确? 我们在代码中定义一个lambda$0这个的函数,最终代码如下所示:

@FunctionalInterface
interface Print<T> {
public void print(T x);
} public class Lambda {
public static void PrintString(String s,
Print<String> print) {
print.print(s);
}
private static void lambda$0(String s) {
}
public static void main(String[] args) {
PrintString("test", (x) -> System.out.println(x));
}
}

上面的代码在编译时不会报错,但是运行时就会报错,因为存在两个lambda$0函数,如下所示,是运行时的错误

Exception in thread "main" java.lang.ClassFormatError: Duplicate method name&signature in class file Lambda
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)

通过javap对上述错误代码进行反编译,反编译之后输出的类的成员如下所示

Compiled from "Lambda.java"
public class Lambda {
public Lambda();
public static void PrintString(java.lang.String, Print<java.lang.String>);
private static void lambda$0(java.lang.String);
public static void main(java.lang.String[]);
private static void lambda$0(java.lang.String);
}

会发现lambda$0出现了两次,那么在代码运行的时候,就不知道去调用哪个,因此就会抛错。

有了上面的内容,可以知道的是Lambda表达式在Java 8中首先会生成一个私有的静态函数,这个私有的静态函数干的就是Lambda表达式里面的内容,因此上面的代码初步可以转化成如下所示的代码

@FunctionalInterface
interface Print<T> {
public void print(T x);
}
public class Lambda {
public static void PrintString(String s, Print<String> print) {
print.print(s);
} private static void lambda$0(String x) {
System.out.println(x);
} public static void main(String[] args) {
PrintString("test", /**lambda expression**/);
}
}

转化成上面的形式之后,那么如何实现调用静态的lambda$0函数呢,在这里可以在以下方法打上断点,可以发现在有lambda表达式的地方,运行时会进入这个函数

 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表达式生成了一个内部类,为了验证是否生成内部类,可以在运行时加上-Djdk.internal.lambda.dumpProxyClasses,加上这个参数后,运行时,会将生成的内部类class码输出到一个文件中

final class Lambda$$Lambda$1 implements Print {
private Lambda$$Lambda$1();
public void print(java.lang.Object);
}

如果运行javap -c -p 则结果如下

final class Lambda$$Lambda$1 implements Print {
private Lambda$$Lambda$1();
Code:
0: aload_0
1: invokespecial #10 // Method java/lang/Object."<init>":()V
4: return public void print(java.lang.Object);
Code:
0: aload_1
1: checkcast #14 // class java/lang/String
4: invokestatic #20 // Method Lambda.lambda$0:(Ljava/lang/String;)V
7: return
}

通过上面的字节码指令可以发现实现上调用的是Lambda.lambda$0这个私有的静态方法

因此最终的Lambda表达式等价于以下形式

@FunctionalInterface
interface Print<T> {
public void print(T x);
}
public class Lambda {
public static void PrintString(String s, Print<String> print) {
print.print(s);
}
private static void lambda$0(String x) {
System.out.println(x);
}
final class $Lambda$1 implements Print{
@Override
public void print(Object x) {
lambda$0((String)x);
}
}
public static void main(String[] args) {
PrintString("test", new Lambda().new $Lambda$1());
}
}

lambda表达式底层处理机制的更多相关文章

  1. Lambda表达式底层分析

    一.我们先看下C#代码下Lamdba表达式的写法 // <summary> /// 写入日志委托 /// </summary> /// <param name=" ...

  2. java8 探讨与分析匿名内部类、lambda表达式、方法引用的底层实现

    问题解决思路:查看编译生成的字节码文件 目录 测试匿名内部类的实现 小结 测试lambda表达式 小结 测试方法引用 小结 三种实现方式的总结 对于lambda表达式,为什么java8要这样做? 理论 ...

  3. C++实现委托机制(三)——lambda表达式封装

    C++.引言:              其实原本没打算写这一章的,不过最后想了想,嗯还是把lambda表达式也一并封装进去,让这个委托也适应lambda表达式的注册.不过在之前还是需要先了解lamb ...

  4. 等待唤醒机制----线程池----lambda表达式

    1.等待唤醒机制 1.1线程间通信 概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同. 比如:线程A用来生成包子的,线程B用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的 ...

  5. 程序猿修仙之路--数据结构之你是否真的懂数组? c#socket TCP同步网络通信 用lambda表达式树替代反射 ASP.NET MVC如何做一个简单的非法登录拦截

    程序猿修仙之路--数据结构之你是否真的懂数组?   数据结构 但凡IT江湖侠士,算法与数据结构为必修之课.早有前辈已经明确指出:程序=算法+数据结构  .要想在之后的江湖历练中通关,数据结构必不可少. ...

  6. Java核心技术-接口、lambda表达式与内部类

    本章将主要介绍: 接口技术:主要用来描述类具有什么功能,而并不给出每个功能的具体实现.一个类可以实现一个或多个接口. lambda表达式:这是一种表示可以在将来的某个时间点执行的代码块的简洁方法. 内 ...

  7. c#封装DBHelper类 c# 图片加水印 (摘)C#生成随机数的三种方法 使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象 c# 制作正方形图片 JavaScript 事件循环及异步原理(完全指北)

    c#封装DBHelper类   public enum EffentNextType { /// <summary> /// 对其他语句无任何影响 /// </summary> ...

  8. python函数和lambda表达式学习笔记

    1. python函数 不同于其他语言,python支持函数返回多个值 为函数提供说明文档:help(函数名)或者函数名.__doc__ def str_max(str1, str2): ''' 比较 ...

  9. 深入探究JVM之方法调用及Lambda表达式实现原理

    @ 目录 前言 正文 解析 分派 静态分派 动态分派 单分派和多分派 动态分派的实现 Lambda表达式的实现原理 MethodHandle 总结 前言 在最开始讲解JVM内存结构的时候有简单分析过方 ...

随机推荐

  1. python使用super()调用父类的方法

    如果要在子类中引用父类的方法,但是又需要添加一些子类所特有的内容,可通过类名.方法()和super()来调用父类的方法,再个性化子类的对应函数. 直接使用类名.方法()来调用时,还是需要传入self为 ...

  2. kali安装搜狗输入法

    安装步骤 依赖1: apt-get install fcitx 依赖2: apt-get install fcitx-libs-qt 重建: apt-get --fix-broken install ...

  3. Redhat7.4 vim编辑无颜色解决

    背景 发现使用vim时, 统一颜色, 不区分关键字 解决 vim /etc/profile 在文件的末尾加入下面这一行 export TERM=xterm

  4. 公网FTP(filezilla)改端口

    背景:我们如果不修改ftp服务器的端口,很容易被别人测试和攻击. 配置要点:服务端端口设置.主被动设置.服务端和客户端防火墙设置 ftp服务器:filezilla ftp server 1.  监听端 ...

  5. topcoder srm 420 div1

    problem1 link 暴力即可.因为即便所有数字的和是50,50所有的不同的划分数只有204226中.所以最长的循环也就这么大. problem2 link 令$f[i][j]$表示有$i$个红 ...

  6. Python3 tkinter基础 LabelFrame Radiobutton 形成两组不相互限制的单选按钮

             Python : 3.7.0          OS : Ubuntu 18.04.1 LTS         IDE : PyCharm 2018.2.4       Conda ...

  7. Python3基础 dict get 在查询不存在的键时,返回指定的内容

             Python : 3.7.0          OS : Ubuntu 18.04.1 LTS         IDE : PyCharm 2018.2.4       Conda ...

  8. Python3基础 response.read 输出网页的源代码

             Python : 3.7.0          OS : Ubuntu 18.04.1 LTS         IDE : PyCharm 2018.2.4       Conda ...

  9. noip模拟【array】

    array by ysy [题目描述] 给定一个长度为n的数列,每次你可以进行以下操作之一: (1)将一个数+a: (2)将一个数-a: (3)将一个数+b: (4)将一个数-b: 你需要将所有数全部 ...

  10. 集训DAYn——组合数学(1)

    组合 又到了我们信息老师讲数学课了,吼吼吼 然后数学老师中途探望了一下,哇塞塞,然后他看到黑板上的题,微妙的笑了. 排列: 从n个数中有序的选出m个数的方案数是多少?第一个数有n种取法,第二个数有n- ...