世界正在缓慢而稳步的改变。这次改变给我们带来了一个新模样的JDK7,java社区也在一直期盼着在JDK8,也许是JDK9中出现一些其他的改进。JDK8的改进目标是填补JDK7实现中的一些空白——部分计划蓝图将被实现,在2013年里,从三个特殊方面提升和优化这门语言:
- 开发效率
- 性能
- 模块化
因此,从明年开始,java会通过在各个平台运行的方式(手机,云端,桌面,服务器等)来作为优化改进的一种方式。接下来,我将把2013年我们所期待的做一个概述——恰好该做新年年度计划——之后,我们将把重点放在提高开发效率的lambda项目上,以及在编写代码中如何将lambda表达式引进。
开发效率
生产效率方面JDK8主要从以下2个目标提升:
-集合(collections)- 通过对集合扩展,让使用时更加简洁
-注解(annotations)- 加强注解支持,允许在上下文中写注解,现在是不能这样用的(如:primitives)
性能
把Fork/Join框架加到JDK7中,是我们转向多核编程的第一步。JDK8通过提供闭包(lambda表达式)支持的方式将这条路线走的更远了。可能影响较大的就是集合部分吧,闭包再加上新的接口和功能将推使java容器到一个新的层次。除了更加增加可读性和代码的简洁性,lambda表达式还使集合操作能充分利用多核处理器特性。
模块化
社区中最让人感兴趣的一块是 jigsaw 项目:这个项目的目的是为JAVA SE平台设计和实现一个标准模块化的系统,然后把这个系统应用到平台本身和JDK。这里我用了过去式的说法是为了那些我们希望摆脱类路径(环境变量)和类载入器,我们不得不把期待留到JAVA9,至于那个时间点,也会因为 jigsaw 项目而被推迟。
我们来看一下2013年java的里程碑:
2013/01/31 M6 功能完成
2013/02/21 M7 开发者预览版本
2013/07/05 M8 最终候选版本
2013/09/09 GA 通用版
除了jigsaw 项目,另外一个让我们兴奋的大变动(在这个版本)将要到来,那就是闭包的支持!在lambda表达式的帮助下,jdk将有了关键性的提升。
Lambdas
首先,我们需要下载个支持lambda的jdk,有两种方式可以获取到:
* 一个用于敢于尝试的人:从sources 源码自己构建
* 快捷版:直接下载编译好的sdk
最初,我使用源码构建,但由于时间原因,再加上和环境变量有关的一些警告,我选择了偷懒的方法:使用已经构建好的jdk。另外一个重要的工具,是文本编辑器用它来写代码。在以前,jdk刚发布后一段时间,一个支持的IDE才产生。但这次不同了,也可能由于openjdk提供的透明和应用广泛的jdk有关。几天前,JetBrain第一个支持java8的IDE发布了。因此,IntelliJ IDEA 12成了第一个支持JDK8的IDE,除此之外的改进呢?处于测试目的,我在win7 x64机器上安装了支持jdk8 b68的IntelliJ 12社区版本。那些喜欢Netbeans的开发者,可以猛戳此处 download下载对lambda支持的包。
调整到合适的心态
想要尝试运用最新的特性编写出更加高效和整洁的代码,你必须了解一下几个新的概念--好吧,至少鄙人需要。什么是lambda表达式?
最简单的看待lambda表达式的方式就是,你可以把它看做一个方法:”它提供一系列的正式的参数和一个通过这些参数来表述逻辑的方法体---它可以是一个表达式或者一个代码段。lambda表达式的参数可以是声名的或者引用的,当这些参数是引用类型的时候,那么这些类型就是源于针对lambda表达式的功能性接口。从返回值来看,一个lambda表达式可以是无返回值的--它们不返回任何结果,或者是有返回值的--在表达式里面的某个执行语句返回一个值。
下面是一个lambda表达式的例子:
- (a) (int a, int b) -> a + b
- (b) (int a, int b) -> {
- if (a > b) {
- return a;
- } else if (a == b) {
- return a * b;
- } else {
- return b;
- }
- }
什么是功能性接口呢?一个功能性接口就是一个只含有抽象方法的接口,只是声名了一个函数。在某些场合下,这个唯一的函数可能是一个带有重载因子的的多态函数,这种情况下,所有的函数对外都是一个函数。除了典型的通过新建和初始化一个类来新建一个接口实例,功能性接口实例还可以通过使用一个lambda表达式、方法、或者构造引用来达到新建实例的效果。下面是一个功能性接口的例子:
- // custom built functional interface
- public interface FuncInterface {
- public void invoke(String s1, String s2);
- }
下面是来自java api的功能性接口:
- java.lang.Comparable
- java.lang.Runnable
- java.util.concurrent.Callable
- java.awt.event.ActionListener
接下来让我们来看看一个线程的启动在future中可能会发生怎么的变化:
旧方式:
- new Thread(new Runnable() {
- @Override
- public void run() {
- for (int i=0; i< 9; i++) {
- System.out.println(String.format("Message #%d from inside the thread!", i));
- }
- }
- }).start();
新方式:
- new Thread(() -> {
- for (int i=0; i< 9; i++) {
- System.out.println(String.format("Message #%d from inside the thread!", i));
- }
- }).start();
即使我还没有写过与java swing,AWT相关的功能,但是我还是可以断定:lambdas肯定会给那些Swing开发者带去很多的便利。
动作监听:
- JButton button = new JButton("Click");
- // NEW WAY:
- button.addActionListener( (e) -> {
- System.out.println("The button was clicked!");
- });
- // OLD WAY:
- button.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- System.out.println("The button was clicked using old fashion code!");
- }
- });
什么是SAM?SAM 是单个抽象方法的替代,因此,直接一点,我们可以说SAM==功能性接口。即使在最初的规范里面,只有一个抽象方法的抽象类被认为是SAM类型的,很多人还是发现/猜出了这样定义的原因。
方法/构造方法 引用
lambdas表达式听起来不错?但是不知为何功能接口带有一定的限制性- 那是否意味着我只能用包含单个抽象方法的接口呢?不见得——JDK8提供了一个别名机制,允许类或者对象的方法“调用”。用一个新增的操作符::可以做到。他可以应用到静态方法或者对象方法的调用。同样也可以应用于构造函数。
参考代码:
- interface ConstructorReference {
- T constructor();
- }
- interface MethodReference {
- void anotherMethod(String input);
- }
- public class ConstructorClass {
- String value;
- public ConstructorClass() {
- value = "default";
- }
- public static void method(String input) {
- System.out.println(input);
- }
- public void nextMethod(String input) {
- // operations
- }
- public static void main(String... args) {
- // constructor reference
- ConstructorReference reference = ConstructorClass::new;
- ConstructorClass cc = reference.constructor();
- // static method reference
- MethodReference mr = cc::method;
- // object method reference
- MethodReference mr2 = cc::nextMethod;
- System.out.println(cc.value);
- }
- }
在接口中的默认方法
这意味着从版本8,JAVA接口可以含有方法体,所以为了使其简单,JAVA将支持多重继承,摆脱了以前比如头痛的问题。并且,通过为接口方法提供默认的实现,可以保证确保添加一个亲的方法不会在实现类中制造混乱。JDK8已经加入了默认方法到接口中如:java.util.Collection 和 java.util.Iterator,并且通过这个功能,可以提供一个机制去更好地在我们真的需要时使用lambdas。
已经加入的主要接口了:
- java.util.stream.Streamable
- java.util.stream.Stream
改进了集合的互动
在我看来,所有lambda工程的改变都是对语言的极大补充,这将会使本语言与当前标准对齐,使其更简单、更简洁,但是这些改变可能使得本语言有最大的效率影响和最大的酷+哇效应,进而使集合框架将会有彻底的改造。但是,这没有集合2框架的概念,我们现在依然必须去做类型探险处理,但是JAVA将有其他重要方面的改变:从外部到内部的迭代。如此,它提供开发者的机制去过滤和用一个优雅的方式去聚合集合,除此之外,还提高了效率。通过提供lambda表达式,将会在内部被执行,这样,就可以充分利用多核处理器的功能了。
让我们考虑以下场景:
a.假如有一个字符串列表,选择该列表的所有对象然后转换成大写字母。这该如何写呢?
旧方法如下:
- //.....
- List inputList = new LinkedList<>();
- List upper = new LinkedList<>();
- // add elements
- for (String currentValue : inputList) {
- if (currentValue != null && currentValue.matches("*")) {
- upper.add(currentValue);
- }
- }
- System.out.println(upper);
新方法:
- //.....
- inputList.stream().filter(x -> (x != null && x.matches("[A-Z0-9]*"))).into(upper);
b.假如你想要把所有提取的字符转换成小写字母。用JDK8的方法做将会像如下这样做:
- / .....
- inputList.stream().filter(x -> (x != null && x.matches("[A-Z0-9]*"))).map(String::toLowerCase).into(upper);
c. 如何从选定的集合中找出字符的数量:
- // .....
- int sumX = inputList.stream().filter(x -> (x != null && x.matches("[A-Z0-9]*"))).map(String::length).reduce(0, Integer::sum);
用到的方法:
- default Stream stream() // java.util.Collection
- Stream filter(Predicate predicate) // java.util.stream.Stream
- IntStream map(IntFunction mapper) //java.util.stream.Stream
d.如果要从集合中取出每个元素然后打印,该怎样做呢?
- //旧方法:
- for (String current : list) {
- System.out.println(current);
- }
- //新方法:
- list.forEach(x -> System.out.println(x));
除了以上提到的功能外,JDK8还有其他有趣的新功能,但为了简洁篇幅,在此不做介绍。更多关于JDK8的信息可以在JDK8 [a href="http://jdk8.java.net/lambda/"]的lambda项目或者 JSR 337网页上得到。
总而言之,Java正在不断地改进,我个人喜欢它未来的方向,另外一个喜欢的点就是当类库开发人员开始采用JDK 8的时候。这将肯定会很有趣的一件事。
译文链接:http://www.oschina.net/translate/far-sight-look-at-jdk8