Java基础学习总结(43)——Java8 Lambda揭秘
再了解了Java 8 Lambda的一些基本概念和应用后, 我们会有这样的一个问题: Lambda表达式被编译成了什么?。 这是一个有趣的问题,涉及到JDK的具体的实现。 本文将介绍OpenJDK对Lambda表达式的转换细节, 读者可以了解Java 8 Lambda表达式背景知识。
Brian Goetz是Oracle的Java语言架构师, JSR 335(Lambda Expression)规范的lead, 写了几篇Lambda设计方面的文章, 其中之一就是Translation of Lambda Expressions。 这篇文章介绍了Java 8 Lambda设计时的考虑以及实现方法。
他提到, Lambda表达式可以通过内部类, method handle, dynamic proxy等方式实现, 但是这些方法各有优劣。 真正要实现Lambda表达式, 必须兼顾两个目标: 一是不引入特定策略,以期为将来的优化提供最大的灵活性, 二是保持类文件格式的稳定。 通过Java 7中引入的invokedynamic (JSR 292), 可以很好的兼顾这两个目标。
invokedynamic 在缺乏静态类型信息的情况下可以支持有效的灵活的方法调用。主要是为了日益增长的运行在JVM上的动态类型语言, 如Groovy, JRuby。
invokedynamic将Lambda表达式的转换策略推迟到运行时, 这也意味着我们现在编译的代码在将来的转换策略改变的情况下也能正常运行。
编译器在编译的时候, 会将Lambda表达式的表达式体 (lambda body)脱糖(desugar) 成一个方法,此方法的参数列表和返回类型和lambda表达式一致, 如果有捕获参数, 脱糖的方法的参数可能会更多一些, 并会产生一个invokedynamic调用, 调用一个call site。 这个call site被调用时会返回lambda表达式的目标类型(functional interface)的一个实现类。 这个call site称为这个lambda表达式的lambda
factory。 lambda factory的Bootstrap方法是一个标准方法, 叫做lambda metafactory。
编译器在转换lambda表达式时, 可以推断出表达式的参数类型,返回类型以及异常, 称之为natural signature, 我们将目标类型的方法签名称之为lambda descriptor, lambda factory的返回对象实现了函数式接口, 并且关联的表达式的代码逻辑, 称之为lambda object。
使用javap查看生成的字节码 javap -c -p -v com/colobu/lambda/chapter5/Lambda1.class:
可以看到, Lambda表达式体被生成一个称之为lambda$0的方法。 看字节码知道它调用System.out.println输出传入的参数。
原lambda表达式处产生了一条invokedynamic #19, 0。它会调用bootstrap方法。
如果Lambda表达式写成Consumer c = (Consumer & Serializable)s -> System.out.println(s);, 则BootstrapMethods的字节码为
它调用的是LambdaMetafactory.altMetafactory,和上面的调用的方法不同。#114 1意味着要实现Serializable接口。
字节码的指令含义可以参考这篇文章:Java bytecode instruction listings。
可以看到, Lambda表达式具体的转换是通过java.lang.invoke.LambdaMetafactory.metafactory实现的, 静态参数依照lambda表达式和目标类型不同而不同。
实际是由InnerClassLambdaMetafactory的buildCallSite来生成。 生成之前会调用validateMetafactoryArgs方法校验目标类型(SAM)方法的参数/和产生的方法的参数/返回值类型是否一致。
上面的代码基本上是InnerClassLambdaMetafactory.buildCallSite的包装,下面看看这个方法的实现:
其中spinInnerClass调用asm框架动态的产生SAM的实现类, 这个实现类的的方法将会调用编译时产生的那个实现方法。 你可以在编译的时候加上参数-Djdk.internal.lambda.dumpProxyClasses, 这样编译的时候会自动产生运行时spinInnerClass产生的类。
下面的代码中,在一个循环中重复生成调用lambda表达式,只会生成同一个lambda对象, 因为只有同一个invokedynamic指令。
既然LambdaMetafactory会使用asm框架生成一个匿名类, 那么这个类的类名有什么规律的。
后缀/中的数字是一个hash值, 那就是类对象的hash值c.getClass().hashCode()。
既然是类中的实实在在的方法,我们就可以直接调用。当然, 你在代码中直接写lambda$0()编译通不过, 因为Lambda表达式体还没有被抽取成方法。
但是在运行中我们可以通过反射的方式调用。 下面的例子使用发射和MethodHandle两种方式调用这个方法。
但是如果反注释capturedV.greeting = "hi"; 则没问题, 因为capturedV没有被重新赋值, 只是它指向的对象的属性有所变化。
这段代码不会产生一个类似”Lambda$0″新方法。 因为LambdaMetafactory会直接使用这个引用的方法。
Java基础学习总结(43)——Java8 Lambda揭秘的更多相关文章
- 尚学堂JAVA基础学习笔记
目录 尚学堂JAVA基础学习笔记 写在前面 第1章 JAVA入门 第2章 数据类型和运算符 第3章 控制语句 第4章 Java面向对象基础 1. 面向对象基础 2. 面向对象的内存分析 3. 构造方法 ...
- JAVA基础学习-集合三-Map、HashMap,TreeMap与常用API
森林森 一份耕耘,一份收获 博客园 首页 新随笔 联系 管理 订阅 随笔- 397 文章- 0 评论- 78 JAVA基础学习day16--集合三-Map.HashMap,TreeMap与常用A ...
- [转帖]java基础学习总结——多态(动态绑定)
https://www.cnblogs.com/xdp-gacl/p/3644035.html 多态的概念 java基础学习总结——多态(动态绑定) 一.面向对象最核心的机制——动态绑定,也叫多态
- java基础学习笔记五(抽象类)
java基础学习总结——抽象类 抽象类介绍
- Java基础学习-- 继承 的简单总结
代码参考:Java基础学习小记--多态 为什么要引入继承? 还是做一个媒体库,里面可以放CD,可以放DVD.如果把CD和DVD做成两个没有联系的类的话,那么在管理这个媒体库的时候,要单独做一个添加CD ...
- Java基础学习中一些词语和语句的使用
在Java基础学习中,我们刚接触Java会遇到一些词和语句的使用不清的情况,不能很清楚的理解它的运行效果会是怎么样的,如:break,continue在程序中运行效果及跳转位置, 1.先来看看brea ...
- Java基础学习笔记总结
Java基础学习笔记一 Java介绍 Java基础学习笔记二 Java基础语法之变量.数据类型 Java基础学习笔记三 Java基础语法之流程控制语句.循环 Java基础学习笔记四 Java基础语法之 ...
- 转载-java基础学习汇总
共2页: 1 2 下一页 Java制作证书的工具keytool用法总结 孤傲苍狼 2014-06-24 11:03 阅读:25751 评论:3 Java基础学习总结——Java对象的序列化和 ...
- java基础学习总结——开篇
java是我学习的第一门编程语言,当初学习java基础的时候下了不少功夫,趁着这段时间找工作之际,好好整理一下以前学习java基础时记录的笔记,当作是对java基础学习的一个总结吧,将每一个java的 ...
- Java基础学习笔记(一)
Java基础学习笔记(一) Hello World 基础代码学习 代码编写基础结构 class :类,一个类即一个java代码,形成一个class文件,写于每个代码的前端(注意无大写字母) XxxYy ...
随机推荐
- 【XSY2988】取石子
题目来源:NOI2018模拟测试赛(二十六) 题解: 设a<b: 可以先考虑a=1的特殊情况,注意到后手的最优策略是跟着另外一个人取,取到最后剩余不到$a+b$时再看奇偶性: 那么很容易想到把所 ...
- Github添加SSHkey
Git详细教程可参考廖雪峰的Git教程 1. 打开 Git Bash,输入cd ~/.ssh——回车(看你是否有了ssh key 密钥,有了就备份): 2. 输入ssh-keygen -t rsa - ...
- 中山纪念中学培训杂题(难的都不在这里面qwq)
来中山纪中半个月了,差不多就要结束了, 写一些之前考试能更正的题解吧,还有一些不是给人做的(比如IOI2018互测.. 备注:我不会的就没有放上来了,所有数学有关的基本上都死了. 所以这里的题目都是相 ...
- (转)C++引用
前言:引用是C++一个很重要的特性,最近看了很多有关引用的资料和博客,故在此对引用的相关知识进行总结 一.什么是引用 引用,顾名思义是某一个变量或对象的别名,对引用的操作与对其所绑定的变量或对象的操作 ...
- COGS——T 438. 烦人的幻灯片
http://www.cogs.pro/cogs/problem/problem.php?pid=438 ★☆ 输入文件:slides.in 输出文件:slides.out 简单对比时间限 ...
- 洛谷 P2700 逐个击破
P2700 逐个击破 题目背景 三大战役的平津战场上,傅作义集团在以北平.天津为中心,东起唐山西至张家口的铁路线上摆起子一字长蛇阵,并企图在溃败时从海上南逃或向西逃窜.为了就地歼敌不让其逃走,毛主席制 ...
- Cocos2d-x 3.4 之 消灭星星 > 第三篇(终) <
***************************************转载请注明出处:http://blog.csdn.net/lttree************************** ...
- aliyun Ubuntu 14.04 64bit OpenJDK Tomcat7 install
my work environment: aliyun Ubuntu 14.04 64位 first phase:apt-get update (it is very important,oth ...
- mybatis和hibernate的区别【转】
第一章 Hibernate与MyBatisHibernate 是当前最流行的O/R mapping框架,它出身于sf.net,现在已经成为Jboss的一部分. Mybatis 是另外一种优秀的 ...
- hadoop相关
执行wordcount 代码 package org.apache.hadoop.examples; import java.io.IOException; import java.util.Iter ...