Java 8:Lambda表达式试水

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,编译:ImportNew - 朱伟杰

译文链接:http://www.importnew.com/1746.html

【如需转载,请在正文中标注并保留原文链接、译文链接和译者等信息,谢谢合作!】

 

关于作者: 朱伟杰

Java开发工程师,业余翻译

查看朱伟杰的更多文章 >>



相关文章

发表评论

Comment form

(*) 表示必填项

8 条评论

  1. 陈亮 说道:

    不好意思,我不太懂这样的好处在哪里,与直接使用一个方法有什么不同吗?

    Thumb up 0 Thumb down 0

  2. lv.yanl 说道:

    如果java8这样实现,还真是有点丑陋。
    还是javascript比较方便,scala也不错。干嘛非要实现一个接口了 ?为了面向对象而面向对象。貌似真没有必要

    Thumb up 0 Thumb down 0

  3. XD 说道:

    Java是静态的,没有接口就无法判断了。这个写法有点像coffeescript?

    Thumb up 0 Thumb down 0

  4. alex 说道:

    看好 java8 的 lambda表达式, 楼上这些顽固不化的家伙

    Thumb up 0 Thumb down 0

  5. friskit 说道:

    楼上的同学,如果一个新东西比以前还要难用,不能因为它新鲜就随便去用它。。。。

    Thumb up 0 Thumb down 0

  6. zjun 说道:

    确实没有看出好在哪里

    Thumb up 0 Thumb down 0

  7. rock 说道:

    可扩展性变强了

    Thumb up 0 Thumb down 0

  8. hw世界 说道:

    其实只是刚开始有点摸不清头绪才觉得难用,就像当初刚接触泛型那样。我试写了几个小demo之后觉得很方便。还有据说对多核处理器有性能优化

    Thumb up 0 Thumb down 0

跳到底部
返回顶部