Java 8 还要有一阵才能发布,而且它会带来一个我非常期待的语言特性:Lambda表达式。
不幸的是,其他的Java平台大的新特性和模块都已经被推迟到了Java 9。不管怎样,引入lambda表达式(如果你喜欢的话,也可以称它为闭包),会使得Java的编程体验更棒。
还有一段时间的等待——不过Java的开发现在是开源的,我们现在可以看一看并且尝试一下。我们开始吧!
下载和安装启用了Lambda的Java 8
最开始,我还以为我需要自己去编译Java 8,因为它还没有发布。不过,让我惊奇的是,在http://jdk8.java.net/lambda/ 已经提供了所有平台可用的二进制版本。因此我直接下载了***的开发者预览版本,然后安装在我的电脑上。
为了确保它是正常运行的,我创建了一个LambadIntro类,它会输出“Hello, World!”,编译然后执行它:
- ~ $ export JAVA_HOME=~/Devtools/Java/jdk1.8.0/
- ~ $ cd spikes/lambda-water
- ~ $ $JAVA_HOME/bin/javac src/net/jthoenes/blog/spike/lambda/LambdaIntro.java
- ~ $ $JAVA_HOME/bin/java -cp src net.jthoenes.blog.spike.lambda.LambdaIntro
- Hello from Java 8!
注意:我这里是用命令行来编译和执行的,因为IDE现在还不支持Java 8。
非Lambda的方式
举这么一个例子,假设我想要遍历一个对象的列表。不过由于我的业务需求,我还需要取得列表项的值和索引。如果用现在版本的Java来做的话,我需要把实际的逻辑和索引放在一起进行处理:
- List list = Arrays.asList("A", "B", "C");
- for (int index = 0; index < list.size(); index++) { String value = list.get(index); String output = String.format("%d -> %s", index, value);
- System.out.println(output);
- }
这样会输出
- 0 -> A
- 1 -> B
- 2 -> C
这样其实也并不坏,但是我这几行代码里做了两件事:控制列表的迭代以及进行了一些(简单)的业务逻辑处理。不过如果使用Lambda表达式的话,它可以帮助我把这两者分开进行处理。
eachWithIndex方法签名
因此,我想实现一个eachWithIndex方法,它可以这样被调用:
- List list = Arrays.asList("A", "B", "C");
- eachWithIndex(list,
- (value, index) -> {
- String output = String.format("%d -> %s", index, value);
- System.out.println(output);
- }
- );
这个方法接收两个参数。***个参数是要处理的列表,第二个参数是一个lambda表达式或者闭包,它表示处理每个列表项的方法。你可以在第3行看到,这个lambda表达式接受两个参数:当前值和当前索引。这两个参数都没有类型声明。Java 8 的编译器会自动推导出参数的类型。在参数的后面,是一个->符号以及处理每个列表项的代码块。
注意:你需要在一个文本编辑器里编写这个方法或者你需要忽略IDE提示的错误信息。
实现eachWithIndex方法
为了使用Java 8 的lambda,你需要声明一个功能接口。功能接口是一种特殊的接口,它只有一个方法——这个方法会被lambda表达式实现。在这个示例里,我需要声明一个接收一个元素和索引并且没有返回值的接口。因此,我定义了如下的接口:
- public static interface ItemWithIndexVisitor<E> {
- public void visit(E item, int index);
- }
通过这个接口,我现在可以实现eachWithIndex方法。
- public static <E> void eachWithIndex(List<E> list, ItemWithIndexVisitor<E> visitor) {
- for (int i = 0; i < list.size(); i++) {
- visitor.visit(list.get(i), i);
- }
- }
这个方法使用了泛型参数<E>,因此传入到visit方法里的元素类型会根据列表的类型进行推导。
使用功能接口的一个好处是,Java里已经有了很多的功能接口。想想关于 java.util.concurrent.Callableinterface的例子。它可以被当作lambda表达式来使用,并且不需要修改 Callable的调用者的代码。这样使得大部分的JDK和框架默认都能够支持lambda表达式。
使用方法引用
另外一个来自于Lambda项目的很有用的东西是方法引用。它是一种复用已有的方法并且把它们打包成一个功能接口对象的方式。假设我们有如下的代码:
- public static <E> void printItem(E value, int index) {
- String output = String.format("%d -> %s", index, value);
- System.out.println(output);
- }
并且我想在eachWithIndex方法里调用它,那么我就可以在我的方法调用里使用::符号:
- eachWithIndex(list, LambdaIntro::printItem);
看起来很不错,并且也很简洁,对吗?
总结
这样就可以使得我的***个lambda表达式例子能够运行。等待了这么长的时间,能够看到闭包运行在我的Java程序里,我实在是太兴奋了。Lambda表达式现在只是在开发者预览的版本上可用。如果你想要了解更多的话,你可以去阅读一下关于Lambda表达式的早期预案的评议,或者你也可以去看看Lambda项目的页面。
我把整个实例代码都上传到gist上了。
英文原文:tataryn