原文:Java 7 Features Which Enable Java 8
It's a truism of the tech industry that developers are never happier than when there's free beer or an opportunity to complain about something on offer.
So despite the efforts of Mark Reinhold and the Java team to involve the community in the roadmap after the Oracle acquisition (the Plan A / Plan B decision), many Java developers feel that Java 7 was "not much of a release".
In this article, I'll try to refute this thesis, by exploring the features in Java 7 which lay the groundwork for the new features in Java 8.
Java has often been criticised for being overly verbose. One of the most common areas where this complaint is expressed is in assignment. In Java 6, we are forced to write assignment statements like this:
Map<String, String> m = new HashMap<String, String>();
This statement contains a lot of redundant information - we should be able to somehow have the compiler figure out more of this by itself, and not require the programmer to be quite so explicit.
In fact, languages like Scala do a large amount of type inference from expressions, and in fact assignment statements can be written as simply as this:
val m = Map("x" -> 24, "y" -> 25, "z" -> 26);
The keyword val indicates that this variable may not be reassigned to (like the keyword final for Java variables). No type information is specified about the variable at all - instead the Scala compiler examines the right-hand side of the assignment and determines the correct type for the variable by looking at which value is being assigned.
In Java 7, some limited type inference capabilities were introduced, and assignment statements can now be written like this:
Map<String, String> m = new HashMap<>();
The key differences between this and the Scala form is that in Scala, values have explicit types, and it is the type of variables that is inferred. In Java 7, the type of variables is explicit, and type information about values is what is inferred.
Some developers have complained that they would have preferred the Scala solution, but it turns out to be less convenient in the context of a major feature for Java 8 - lambda expressions.
In Java 8, we can write a function which adds 2 to an integer like this:
Function<Integer, Integer> fn = x -> x + 2;
The interface Function is new with Java 8 - it resides in the package java.util.function, along with specialized forms for primitive types. However, we've chosen this syntax as it is very similar to the Scala equivalent and allows the developer to see the similarities more easily.
By explicitly specifying the type of fn as a Function which takes one Integer argument and returns another Integer, then the Java compiler is able to infer the type of the parameter x - Integer. This is the same pattern that we saw in Java 7 diamond syntax - we specify the types of variables, and infer the type of values.
Let's look at the corresponding Scala lambda expression:
val fn = (x : Int) => x + 2;
Here, we have to explicitly specify the type of the parameter x, as we don't have the precise type of fn, and so we have nothing to infer from. The Scala form is not hugely difficult to read, but the Java 8 form has a certain cleanliness of syntax which can be directly traced back to the diamond syntax of Java 7.
-
Method Handles
Method Handles are simultaneously the most important new feature of Java 7, and the one which is least likely to feature in the day-to-day life of most Java developers.
A method handle is a typed reference to a method for execution. They can be thought of as "typesafe function pointers" (for developers familiar with C/C++) or as "Core Reflection reimagined for the modern Java developer".
Method handles play a huge part in the implementation of lambda expressions. Early prototypes of Java 8 had each lambda expression converted to an anonymous inner class at compile time.
More recent betas are more sophisticated. Let's start by recalling that a lambda expression (at least in Java) comprises a function signature (which in the method handles API will be represented by a MethodType object) and a body, but not necessarily a function name.
This suggests that we could convert the lambda expression into a synthetic method which has the correct signature and which contains the body of the lambda. For example, our example:
Function<Integer, Integer> fn = x -> x + 2;
is turned by the Java 8 compiler into a private method with this bytecode:
private static java.lang.Integer lambda$0(java.lang.Integer); descriptor: (Ljava/lang/Integer;)Ljava/lang/Integer; flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC Code: stack=2, locals=1, args_size=1 0: aload_0 1: invokevirtual #13 // Method java/lang/Integer.intValue:()I 4: iconst_2 5: iadd 6: invokestatic #8 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 9: areturn
This has the correct signature (takes in an Integer, and returns another one), and semantics. To use this lambda expression, we will take a method handle which refers to it and use it to build an object of the appropriate type, as we'll see in the next feature we discuss.
-
invokedynamic
The final feature of Java 7 that opens the door for Java 8 is even more esoteric than method handles. This is the new bytecode invokedynamic - the first new bytecode to be added to the platform since Java 1.0. This feature is almost impossible for Java developers to make use of in version 7, because version 7 javac will not, under any circumstances, emit a classfile which contains it.
Instead, the bytecode was designed for use by developers of non-Java languages, such as JRuby, which require much more dynamic dispatch than Java. To see how invokedynamic works, let's discuss how Java's method calls are compiled into bytecode.
A standard Java method call will be turned into a piece of JVM bytecodes which is often referred to as a call site. This comprises a dispatch opcode (such as invokevirtual, for regular instance method calls) and a constant (an offset into the Constant Pool of the class) which indicates which method is to be called.
The different dispatch opcodes have different rules that govern how method lookup is done, but until Java 7 the constant was always a straightforward indication of which method was to be called.
invokedynamic is different. Instead of providing a constant which directly indicates which method is to be called, invokedynamic instead provides an indirection mechanism that allows user code to decide which method to call at runtime.
When an invokedynamic site is first encountered, it does not have a known target yet. Instead, a method handle (called a bootstrap method) is invoked. This bootstrap method returns a CallSite object, which contains another method handle, which is the actual target of the invokedynamic call.
1) invokedynamic site encountered in the execution stream (initially unlinked) 2) Call bootstrap method and return a CallSite object 3) CallSite object contains a method handle (the target) 4) Invoke the target method handle
The bootstrap method is the way in which user code chooses which method needs to be called. For lambda expressions, the platform uses a library-supplied bootstrap method called a lambda meta-factory.
This has static arguments which contain a method handle to the synthesized method (see last section), and the correct signature for the lambda.
The meta-factory returns a CallSite that contains a method handle which will in turn return an instance of the correct type that the lambda expression has been converted to. So a statement like:
Function<Integer, Integer> fn = x -> x + 2;
is converted to an invokedynamic call like this:
Code: stack=4, locals=2, args_size=1 0: invokedynamic #2, 0 // InvokeDynamic #0:apply:()Ljava/util/function/Function; 5: astore_1
The invokedynamic bootstrap method is the static method LambdaMetafactory.metafactory(), which returns a CallSite object which is linked to a target method handle, which will return an o bject which implements the Function interface.
When the invokedynamic instruction is complete, an object which implements Function and which has the lambda expression as the contents of its apply() method is sat on top of the stack, and the rest of the code can proceed normally.
-
Conclusion
Getting lambda expressions into the Java platform was always going to be a challenging endeavour, but by ensuring that the proper groundwork was in place, Java 7 eased that effort considerably. Plan B not only provided developers with the early release of Java 7 but also allowed core technologies to be made fully road-tested before their use in Java 8 and especially in lambda expressions.
About the Author
Ben Evans is the CEO of jClarity, a startup which delivers performance tools to help development & ops teams. He is an organizer for the LJC (London JUG) and a member of the JCP Executive Committee, helping define standards for the Java ecosystem. He is a Java Champion; JavaOne Rockstar; co-author of “The Well-Grounded Java Developer” and a regular public speaker on the Java platform, performance, concurrency, and related topics.
相关推荐
从多用户协作到全异步数据推送,异步Ajax技术正引领着Web应用进入新的发展阶段。同时,文章提到了一系列技术工具和框架,如Dojo、DWR和ICEfaces,这些工具和技术的结合能够帮助开发者轻松创建具备实时更新和用户间...
本文旨在为那些拥有一定JAVA基础知识,尤其是熟悉J2ME环境并理解MIDLET和CANVAS概念的开发者,提供一份详尽的指南,引领他们探索移动3D开发的奥秘。 #### 版权声明与文档结构 在正式展开之前,必须提及的是,这份...
scratch少儿编程逻辑思维游戏源码-城堡战争.zip
内容概要:本文档汇集了来自字节跳动、腾讯、金山WPS、跟谁学和百度等大厂的Go工程师面试题,涵盖广泛的技术领域。主要包括Go语言特性(如goroutine调度、channel机制)、操作系统(进程间通信、线程调度)、计算机网络(TCP/IP协议栈、HTTP协议)、数据结构与算法(排序算法、LRU缓存)、数据库(MySQL索引优化、Redis内部机制)、分布式系统(负载均衡、服务发现)等方面的知识点。通过这些问题,不仅考察应聘者的理论基础,还测试其实际项目经验和技术深度。 适合人群:有一定Go语言编程经验和计算机基础知识的开发者,特别是准备应聘互联网大厂的中级及以上水平的后端工程师或全栈工程师。 使用场景及目标:①帮助求职者全面复习Go语言及其相关领域的核心概念;②为面试官提供有价值的参考题目,确保候选人具备解决复杂问题的能力;③指导工程师深入理解并掌握企业级应用开发所需的关键技能。 阅读建议:由于题目覆盖面广且难度较高,建议读者结合自身情况选择重点复习方向,同时配合实际编码练习加深理解。对于每个知识点,不仅要记住答案,更要理解背后的原理,这样才能在面试中灵活应对各种变体问题。
scratch少儿编程逻辑思维游戏源码-堡垒之夜(吃鸡游戏).zip
少儿编程scratch项目源代码文件案例素材-派.zip
scratch少儿编程逻辑思维游戏源码-Scratch 冒险.zip
2025 飞特舵机, Arduino版本
scratch少儿编程逻辑思维游戏源码-躲避.zip
内容概要:本文详细介绍了利用PFC5.0进行纤维混凝土三点弯曲模拟的方法。首先,作者展示了如何通过定义纤维的体积含量、长度、半径和刚度等关键参数来构建纤维网络。接着,描述了三点弯曲加载的具体实现方式,包括加载速率控制和终止条件设定。最后,提供了后处理方法,如绘制并导出力-位移曲线图,以便于分析材料破坏机制。文中还给出了若干实用建议,如纤维半径的选择范围、加载速率的初始值以及不同类型纤维的接触模型选择。 适合人群:从事材料科学尤其是混凝土材料研究的专业人士,以及对离散元法和数值模拟感兴趣的科研工作者。 使用场景及目标:适用于希望深入了解纤维混凝土力学性能的研究人员,旨在帮助他们掌握PFC5.0软件的操作技巧,优化模拟参数设置,提高实验效率。 其他说明:文中提供的代码片段可以直接应用于实际项目中,同时附带了一些实践经验分享,有助于初学者快速入门并避免常见错误。
少儿编程scratch项目源代码文件案例素材-生存V1(有BAG).zip
少儿编程scratch项目源代码文件案例素材-披萨机器人.zip
少儿编程scratch项目源代码文件案例素材-气球滑雪板.zip
少儿编程scratch项目源代码文件案例素材-使命召唤(苏联插旗).zip
1. GPIO模拟I2C 实战项目,根据正点原子 STM32F407ZGT6 进行更改; 2. 可适配STM32、GD32、HC32等MCU;
scratch少儿编程逻辑思维游戏源码-百米冲刺.zip
内容概要:本文档汇总了蓝桥杯历年试题及练习资源,涵盖编程类试题精选、硬件与单片机试题、练习资源与题库以及备考建议。编程类试题精选包括基础算法题(如数组求和、质因数分解)、经典算法案例(如最大子序列和、兰顿蚂蚁模拟)和数据结构应用(如字符全排列)。硬件与单片机试题主要涉及客观题考点,如BUCK电路和电源设计。练习资源与题库部分介绍了真题平台(如Dotcpp、CSDN专题)和专项训练包(如Python题库、Java百题集、C++真题解析)。备考建议分为分阶段练习(新手阶段、进阶提升)和模拟实战(如使用Dotcpp估分系统进行限时训练),强调按年份和组别分类练习,强化代码实现与调试能力。; 适合人群:准备参加蓝桥杯竞赛的学生及编程爱好者。; 使用场景及目标:①针对不同编程语言和难度级别的题目进行专项训练;②通过历年真题和模拟实战提高解题速度和准确性;③掌握算法设计、数据结构应用及硬件基础知识。; 阅读建议:此文档提供了丰富的试题和练习资源,建议根据自身水平选择合适的题目进行练习,并结合真题平台的估分系统和社区开源代码进行对比优化,逐步提升编程能力和竞赛水平。
内容概要:本文详细介绍了30kW储能PCS(电力转换系统)原理图的设计要点及其量产化过程中需要注意的技术细节。首先阐述了储能PCS的基本概念和重要性,接着深入探讨了主拓扑结构的选择,特别是双级式结构的优势以及关键组件如IGBT的驱动时序配置。随后讨论了控制算法的智能化改进,包括加入前馈补偿以提高系统的稳定性。此外,还强调了EMC设计、PCB布局、元件选择等方面的注意事项,并分享了一些实际生产中遇到的问题及解决方案。最后提到了自动化测试方法和散热管理策略,确保产品在各种环境下的可靠运行。 适合人群:从事储能系统设计、电力电子产品研发的工程师和技术人员。 使用场景及目标:帮助读者掌握30kW储能PCS从原理图设计到量产实施的全流程关键技术,提升产品的性能和可靠性,避免常见错误。 其他说明:文中提供了具体的代码片段和实践经验,有助于理解和应用相关理论。
少儿编程scratch项目源代码文件案例素材-喷气包多德.zip
内容概要:本文深入探讨了齿轮啮合性能及其动态特性,特别是直齿轮的基础参数计算、渐开线绘制以及接触力仿真的具体实现。首先介绍了齿轮的基本参数如模数、齿数、压力角等,并给出了具体的计算实例。接着详细讲解了如何利用Python进行渐开线的数学建模并绘图展示,强调了这种曲线对于确保齿轮平稳传动的重要性。然后讨论了齿轮在啮合过程中接触力的变化规律,提供了简化的Python代码来模拟这一现象。最后指出,在实际工程项目中应当借助专业的软件包如PyDy或ADAMS来进行更加精确的动力学分析,同时肯定了自行编写代码的价值在于能够更好地理解和排查问题。 适合人群:机械工程领域的研究人员、工程师以及相关专业的学生。 使用场景及目标:①帮助读者掌握齿轮基本理论知识;②指导读者运用Python编程技能完成简单的齿轮性能分析任务;③为后续深入研究提供思路和技术支持。 阅读建议:由于文中涉及较多的专业术语和数学公式,建议读者提前复习相关基础知识,并尝试运行提供的代码片段加深理解。此外,对于想要进一步探索该领域的读者来说,可以参考文末提到的专业工具包进行更复杂的研究。