再了解了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揭秘的更多相关文章

  1. 尚学堂JAVA基础学习笔记

    目录 尚学堂JAVA基础学习笔记 写在前面 第1章 JAVA入门 第2章 数据类型和运算符 第3章 控制语句 第4章 Java面向对象基础 1. 面向对象基础 2. 面向对象的内存分析 3. 构造方法 ...

  2. JAVA基础学习-集合三-Map、HashMap,TreeMap与常用API

    森林森 一份耕耘,一份收获 博客园 首页 新随笔 联系 管理 订阅 随笔- 397  文章- 0  评论- 78  JAVA基础学习day16--集合三-Map.HashMap,TreeMap与常用A ...

  3. [转帖]java基础学习总结——多态(动态绑定)

    https://www.cnblogs.com/xdp-gacl/p/3644035.html 多态的概念 java基础学习总结——多态(动态绑定) 一.面向对象最核心的机制——动态绑定,也叫多态

  4. java基础学习笔记五(抽象类)

    java基础学习总结——抽象类 抽象类介绍

  5. Java基础学习-- 继承 的简单总结

    代码参考:Java基础学习小记--多态 为什么要引入继承? 还是做一个媒体库,里面可以放CD,可以放DVD.如果把CD和DVD做成两个没有联系的类的话,那么在管理这个媒体库的时候,要单独做一个添加CD ...

  6. Java基础学习中一些词语和语句的使用

    在Java基础学习中,我们刚接触Java会遇到一些词和语句的使用不清的情况,不能很清楚的理解它的运行效果会是怎么样的,如:break,continue在程序中运行效果及跳转位置, 1.先来看看brea ...

  7. Java基础学习笔记总结

    Java基础学习笔记一 Java介绍 Java基础学习笔记二 Java基础语法之变量.数据类型 Java基础学习笔记三 Java基础语法之流程控制语句.循环 Java基础学习笔记四 Java基础语法之 ...

  8. 转载-java基础学习汇总

    共2页: 1 2 下一页  Java制作证书的工具keytool用法总结 孤傲苍狼 2014-06-24 11:03 阅读:25751 评论:3     Java基础学习总结——Java对象的序列化和 ...

  9. java基础学习总结——开篇

    java是我学习的第一门编程语言,当初学习java基础的时候下了不少功夫,趁着这段时间找工作之际,好好整理一下以前学习java基础时记录的笔记,当作是对java基础学习的一个总结吧,将每一个java的 ...

  10. Java基础学习笔记(一)

    Java基础学习笔记(一) Hello World 基础代码学习 代码编写基础结构 class :类,一个类即一个java代码,形成一个class文件,写于每个代码的前端(注意无大写字母) XxxYy ...

随机推荐

  1. php && 运算符使用说明

    “&&” 运算符的用法: ;;echo $test;//输出:000

  2. [TJOI2015]线性规划

    题目:洛谷P3973.BZOJ3996. 题目大意:给你n,参数b[][]和c[](里面的数均>0),要你求一个数组A[](0/1,1表示选择),已知:1. 若同时选择X和Y,获得B[x][y] ...

  3. BZOJ 5020 [THUWC2017]Drown in the math ocean (LCT+求导)

    题目大意: 太长了略 洛谷题面传送门 嗯,数学题 感觉考试要是出这种题我就死翘翘了[逃 不用想都知道要$LCT$维护断边连边,但询问该如何处理呢 利用题目给出的公式 $f(x)=\sum_{i=0}^ ...

  4. URL中一些特殊符号的替代符

    下表中列出了一些URL特殊符号及编码 十六进制值 1.+ URL 中+号表示空格 %2B 2.空格 URL中的空格可以用+号或者编码 %20 3./ 分隔目录和子目录 %2F 4.? 分隔实际的 UR ...

  5. jsonp实现原理

    jquery 封装在 ajax方法 里面的jsonp jsonp跨域的原理       1:使用script 标签发送请求,这个标签支持跨域访问       2:在script 标签里面给服务器端传递 ...

  6. webpack操作基础

    webpack 是一个前端加载/打包工具,根据模块的依赖关系进行静态分析,并依根据规则生成对应的静态资源

  7. Qt 3D教程(三)实现对模型材质參数的控制

    Qt 3D教程(三)实现对模型材质參数的控制 蒋彩阳原创文章,首发地址:http://blog.csdn.net/gamesdev/article/details/47131841.欢迎同行前来探讨. ...

  8. _00017 Kafka的体系结构介绍以及Kafka入门案例(0基础案例+Java API的使用)

    博文作者:妳那伊抹微笑 itdog8 地址链接 : http://www.itdog8.com(个人链接) 博客地址:http://blog.csdn.net/u012185296 博文标题:_000 ...

  9. Android生命周期里你也许不知道的事

    Android生命周期预计连刚開始学习的人都再熟悉只是的东西了,但这里我抛出几个问题.也许大家曾经没有想过或者可能认识的有些错误. 一.当A启动B时,A和B生命周期方法运行的先后顺序是如何的?当按返回 ...

  10. angular4(3)angular脚手架引入scss

    scss..sass....sccc...ssss...ccccc......MMP················· 先说下scss和sass的异同: SCSS 是 Sass 3 引入新的语法,其语 ...