java lambda 实现_Java 8 Lambda实现原理分析

PDF文档已上传Github

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

@FunctionalInterfaceinterface Print{public voidprint(T x);

}public classLambda {public static void PrintString(String s, Printprint) {

print.print(s);

}public static voidmain(String[] args) {

PrintString("test", (x) ->System.out.println(x));

}

}

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

@FunctionalInterfaceinterface Print{public voidprint(T x);

}class Lambda$$0 implements Print{

@Overridepublic voidprint(String x) {

System.out.println(x);

}

}public classLambda {public static voidPrintString(String s,

Printprint) {

print.print(s);

}public static voidmain(String[] args) {

PrintString("test", new Lambda$$0());

}

}

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

@FunctionalInterfaceinterface Print{public voidprint(T x);

}public classLambda {final class Lambda$$0 implements Print{

@Overridepublic voidprint(String x) {

System.out.println(x);

}

}public static voidPrintString(String s,

Printprint) {

print.print(s);

}public static voidmain(String[] args) {

PrintString("test", new Lambda().new Lambda$$0());

}

}

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

@FunctionalInterfaceinterface Print{public voidprint(T x);

}public classLambda {public static voidPrintString(String s,

Printprint) {

print.print(s);

}public static voidmain(String[] args) {

PrintString("test", new Print() {

@Overridepublic voidprint(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 classLambda {publicLambda();public static void PrintString(java.lang.String, Print);public static voidmain(java.lang.String[]);private static void lambda$0(java.lang.String);

}

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

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

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

@FunctionalInterfaceinterface Print{public voidprint(T x);

}public classLambda {public static voidPrintString(String s,

Printprint) {

print.print(s);

}private static void lambda$0(String s) {

}public static voidmain(String[] args) {

PrintString("test", (x) ->System.out.println(x));

}

}

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

Exception in thread "main" java.lang.ClassFormatError: Duplicate method name&signature in classfile 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 classLambda {publicLambda();public static void PrintString(java.lang.String, Print);private static void lambda$0(java.lang.String);public static voidmain(java.lang.String[]);private static void lambda$0(java.lang.String);

}

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

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

@FunctionalInterfaceinterface Print{public voidprint(T x);

}public classLambda {public static void PrintString(String s, Printprint) {

print.print(s);

}private static void lambda$0(String x) {

System.out.println(x);

}public static voidmain(String[] args) {

PrintString("test", /**lambda expression**/);

}

}

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

public staticCallSite metafactory(MethodHandles.Lookup caller,

String invokedName,

MethodType invokedType,

MethodType samMethodType,

MethodHandle implMethod,

MethodType instantiatedMethodType)throwsLambdaConversionException {

AbstractValidatingLambdaMetafactory mf;

mf= newInnerClassLambdaMetafactory(caller, invokedType,

invokedName, samMethodType,

implMethod, instantiatedMethodType,false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);

mf.validateMetafactoryArgs();returnmf.buildCallSite();

}

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

final class Lambda$$Lambda$1 implementsPrint {private Lambda$$Lambda$1();public voidprint(java.lang.Object);

}

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

final class Lambda$$Lambda$1 implementsPrint {private Lambda$$Lambda$1();

Code:0: aload_01: invokespecial #10 //Method java/lang/Object."":()V

4: return

public voidprint(java.lang.Object);

Code:0: aload_11: checkcast #14 //class java/lang/String

4: invokestatic #20 //Method Lambda.lambda$0:(Ljava/lang/String;)V

7: return}

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

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

@FunctionalInterfaceinterface Print{public voidprint(T x);

}public classLambda {public static void PrintString(String s, Printprint) {

print.print(s);

}private static void lambda$0(String x) {

System.out.println(x);

}final class $Lambda$1 implementsPrint{

@Overridepublic voidprint(Object x) {

lambda$0((String)x);

}

}public static voidmain(String[] args) {

PrintString("test", new Lambda().new $Lambda$1());

}

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/381372.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

unity game和scene效果不一样_KTV装修设计:如何让消费者体验到不一样的KTV娱乐效果...

现代KTV装修设计要尽显奢华与高贵,但起到吸引消费者的却是浓烈的欢快氛围和愉悦的歌唱体验.KTV想要有一个好的装修效果,需要了解各方面的细节问题.下面怡元小编讲述如何设计能让消费者体验到不一样的KTV娱乐效果?1、氛围设计在KTV装修设计中,氛围设计非常考究,尤其是消费者进入…

SQL學習

1.模糊表名的联合查询..create table tz2008_1_1(id int,name varchar(50))insert into tz2008_1_1 select 1,acreate table tz2008_1_2(id int,name varchar(50))insert into tz2008_1_2 select 2,bcreate table tz2008_1_3(id int,name varchar(50))insert into tz2008_1_3 s…

feather 设置坐标刻度_Matlab中将坐标轴放在原点位置

转载一篇文章,原文链接:https://blog.csdn.net/xiaobiyin9140/article/details/84519419​blog.csdn.net需求使用matlab画图:设置y轴位置,使y轴在x轴的中间示例画一个sigmoid函数MATLAB代码x-10:0.1:10; ysigmf(x,[1 0]); plot(…

hana数据库导入mysql_【SAP HANA】新建表以及操作数据(3)

账号和数据库都创建好之后,接下来就可以创建表了。来见识一下这个所谓“列式”存储方式的表是长啥样的!一、可视化新建表然后输入所需栏位,设置好类型和长度:上图右上角可以看到类型是Column Store,代表列式存储&#…

(转)Asp.net 中 Get和Post 的用法

单form的提交有两种方式&#xff0c;一种是get的方法&#xff0c;一种是post 的方法.看下面代码,理解两种提交的区别: <form id"form1" method"get" runat"server"> <div> 你的名字<asp:TextBox ID"name" ru…

matlab lu分解求线性方程组_计算方法(二)直接三角分解法解线性方程组

封面是WH2里春希在编辑部的上司麻理前辈&#xff0c;有一说一&#xff0c;这条线的第一次H有点恶趣味&#xff0c;不是很喜欢。一&#xff1a;概述矩阵分解我学过的挺多种&#xff0c;比如极分解&#xff0c;谱分解&#xff0c;满秩分解&#xff0c;正交三角分解还有这里的直接…

java pdf添加图片水印图片_Java 在PDF中添加文本水印、图片水印

(推荐)方式1&#xff1a;创建Maven项目程序&#xff0c;通过maven仓库下载导入。以IDEA为例&#xff0c;新建Maven项目&#xff0c;在xml文件中配置maven仓库路径&#xff0c;并指定spire.cloud.sdk的依赖&#xff0c;如下&#xff1a;com.e-icebluecloudhttp://repo.e-iceblue…

html弹出保存文件对话框_有没有遇到过CAD文件损坏或打不开的情况?养成这个习惯很重要...

经常使用CAD制图&#xff0c;难免会遇到CAD文件损坏或者打不开的情况&#xff0c;遇到这种情况&#xff0c;我们会想尽办法来恢复文件&#xff0c;而最有效的办法之一就是从备份文件中恢复我们的图形&#xff0c;因此在制图过程中&#xff0c;我们应养成备份的好习惯&#xff0…

用javascript读取xml,并进行修改xml数据,解决保存没有权限问题

从 fsdy2000&#xff08;乡愁&#xff09;提供的方法获得思路&#xff0c;参考公司web gis 脚本实现 http://topic.csdn.net/t/20060105/15/4499889.html可以这样&#xff1a; 在脚本中修改xml文档后。再用xmlhttp进行页面申请。 xmlhttp new Acti…

linux java uml_简单实用UML关系图解

一句话UML&#xff0c;再记不住就要DPP了&#xff1a;关系图解代码备注1&#xff1a;继承关系(Generalization)2&#xff1a;实现关系(Realization)3&#xff1a;依赖关系(Dependency)方法的参数、局部变量、返回值4&#xff1a;关联关系(Association)互为类属性5&#xff1a;方…

python接口和抽象类的区别_接口和抽象类有什么区别?

最近团队在招人&#xff1a;阿里新零售事业群CBU技术部招Java高级&专家&#xff0c;团队主要负责B2B工业品牌业务&#xff0c;坐标杭州滨江&#xff0c;有兴趣的小伙伴私戳我。 ******************************************** 以下是原文&#xff1a; 本身的设计目的就是不…

版本控制工具SVN+Apache整合

一 SVN服务器端基础配置 1.创建文件库&#xff0c;开启服务 假设SVN服务器安装在D盘根目录下&#xff0c;文件库创建为D盘下的repository_svn文件夹。 sc create svnserve binpath "D:\svn-win32-1.5.0\bin\svnserve.exe --service --root D:\repository_svn" displa…

linux scrapy 定时任务_Linux定时任务给心爱的小姐姐发情书

计划任务基本概述什么是crond?crond就是计划任务&#xff0c;类似于我们平时生活中的闹钟&#xff0c;定点执行。为什么要用crond?计划任务主要是做一些周期性的任务&#xff0c;比如: 凌晨3点定时备份数据。或11点开启网站抢购接口&#xff0c;12点关闭抢占接口。计划任务主…

JAVA中修改顺序表中的元素_java – 在列表中查找元素并使用stream()更改它

如果您的目标是只找到一个元素,那么您可以这样做MyItem item l.stream().filter(x -> x.getValue() > 10).findAny() // here we get an Optional.orElseThrow(() -> new RuntimeException("Element 10 wasnt found"));item.setAnotherValue(4);在Java 9中…

产品经理的职责(转)

1、市场调研 市场调研是指研究市场以了解客户需求、竞争状况及市场力量&#xff08;market forces&#xff09;&#xff0c;其最终目标是发现创新或改进产品的潜在机会。 可以通过下面的方式进行市场调研&#xff1a; 与用户和潜在用户交流 与直接面对客户的一线同事如销售、客…

初中文化能学编程吗_网页编程课程来了,确定不来pick一下!!!|科创辅学进行时...

KE CHUANG FU XUE科创辅学天天用手机&#xff0c;各种app 半夜不睡觉&#xff0c;只会网上浪醒醒&#xff0c;少年&#xff0c;别玩了不要再搞这些花里胡哨的东西了&#xff01;要学会用魔法打败魔法上一周&#xff0c;我们跟着夏老师学习了Arduino单片机编程基础这一周开源软…

java activemq 断线_java - 防止ActiveMQ重新连接失败时自动退出 - SO中文参考 - www.soinside.com...

我有一个小型的spring-boot应用程序&#xff0c;该应用程序连接到ActiveMQ上的一个或多个主题&#xff0c;这些主题在启动时在应用程序的application.properties文件中设置-然后将这些消息发送到数据库。这一切都很好&#xff0c;但是在尝试实施故障转移时遇到一些问题-基本上&…

找不到显示桌面的快捷方式怎么办|显示桌面的快捷方式找不到解决方法|显示桌面代码|...

找不到显示桌面的快捷方式怎么办|显示桌面的快捷方式找不到解决方法|显示桌面代码| 新建记事本把一下代码拷进去&#xff0c;保存成scf格式&#xff0c;然后拖到状态栏图标就可以了 [Shell]Command2IconFileexplorer.exe,3[Taskbar]CommandToggleDesktop 转载于:https://www.c…

python输入一个包含若干自然数的列表_关于python的十一道练习

关于python的十一道练习 1.编写程序&#xff0c;输入一个自然数字符串&#xff0c;然后输出各位数字之和。例如&#xff0c;输入字符串1234&#xff0c;输出10. 1 def sums1(): #第一题 2 strsinput(请输入一个自然数字符串&#xff1a;)3 num04 for i inrange(0,len(strs)):5 …

怎样获取java枚举的名称_如何从Java中的字符串值获得枚举值?

假设我有一个枚举public enum Blah {A, B, C, D}我想找到一个字符串的枚举值&#xff0c;例如"A"&#xff0c;它是Blah.A。怎么可能做到这一点&#xff1f;我需要的方法是Enum.valueOf()吗&#xff1f;如果是的话&#xff0c;我怎么用这个&#xff1f;是的&#xff0…