Java 8必将掀起Java函数式编程热潮-IT168 技术开发专区

【IT168 专稿】Java 8给Java带来了一场变革。很明显,这个版本是过去十年以来推出的最具份量的Java更新,其中囊括了海量新特性,包括默认方法、方法与构造函数引用以及Lambda函数等等。

  其中最有趣的一项特性当数全新java.util.streamAPI,它作为Javadoc状态存在,能够对元素流进行函数式操作,例如在集合中进行map-reduce变换。

Java 8掀起Java函数式编程热潮

  将这个新API与Lambda表达式相结合,我们就得到了一条简洁但却强大的语法,能够对应用程序中的代码进行大幅简化。

  就以表面上看起来相当简单的集合过滤任务为例。在这一实例中,我们如下所示创建Message Collection type:

  创建一个Messages Collection

List&lt;Message&gt;&nbsp;messages&nbsp;=&nbsp;new&nbsp;ArrayList&lt;&gt;();&nbsp;</p><p>messages.add(new&nbsp;Message("aglover",&nbsp;"foo",&nbsp;56854));&nbsp;</p><p>messages.add(new&nbsp;Message("aglover",&nbsp;"foo",&nbsp;85));&nbsp;</p><p>messages.add(new&nbsp;Message("aglover",&nbsp;"bar",&nbsp;9999));&nbsp;</p><p>messages.add(new&nbsp;Message("rsmith",&nbsp;"foo",&nbsp;4564));

  在这个集合中,我打算将delay(第三个构造函数参数)在3000秒以上作为条件对Message进行全面过滤。在Java 8之前的版本中,大家可以用以下方式表达这类逻辑:

  传统过滤方式

for&nbsp;(Message&nbsp;message&nbsp;:&nbsp;messages)&nbsp;</p><p>{&nbsp;&nbsp;&nbsp;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(message.delay&nbsp;&gt;&nbsp;3000)&nbsp;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(message);&nbsp;&nbsp;&nbsp;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</p><p>}

  不过在Java 8中,这项工作将变得更加简单明了。集合如今支持stream方法,它能够将底层数据结构转化为可重复的对象流,从而实现使用Lambda表达式的全新函数式操作。大多数此类操作都可以被串连起来。这些可串连方法被称为intermediate,那些无法被串连的方法则被表示为terminal。

  简要来讲,Lambda表达式与匿名类基本相似,只不过摒弃了大量语法限制。举例来说,如果大家在查看Javadoc以寻找Stream中filter方法的对应参数,各位会发现它拥有一个Predicate type。不过我们不需要像在Java 8之前的版本中那样利用匿名类来实现这一对接。因此,Predicate Lambda表达式能够过滤掉所有数值高于3000的条目,如下所示:

  Lambda表达式

x&nbsp;-&gt;&nbsp;x.delay&nbsp;&gt;&nbsp;3000

  其中的x正是被传送至集合流内每一个值的参数,而->符号右侧的所有内容都作为表达式估值。将这些结合起来,就成了Java 8中的处理方式:

  Lambda表达式流

messages.stream().filter(m&nbsp;-&gt;&nbsp;m.delay&nbsp;&gt;&nbsp;3000).forEach(item&nbsp;-&gt;&nbsp;System.out.println(item));

  有趣的是,由于Java 8中的其它一些新特性,我们还可以对forEach的Lambda表达式进行进一步简化:

  Lambda表达式流还能进一步简化

messages.stream().filter(m&nbsp;-&gt;&nbsp;m.delay&nbsp;&gt;&nbsp;3000).forEach(System.out::println);

  由于forEach Lambda表达式的参数仅仅单纯作用于println,Java 8现在允许我们直接对参数进行整体对接。

  之前我曾经提到过,集合流允许大家将各个Lambda表达式串连起来——在上面的例子中,filter方法属于一项intermediate方法,而forEach则是一项terminal方法。其它能够为函数程序员快速识别出的intermediate方法还包括:map、flatMap以及reduce等,这里就不一一列举了。

  具体来讲,我希望找到Message当中所有延迟周期超过3000秒的条目并计算它们的总计延迟时长。如果没有函数魔法作为辅助,我只能如下进行:

  普通Java写法

long&nbsp;totalWaitTime&nbsp;=&nbsp;0;&nbsp;for&nbsp;(Message&nbsp;message&nbsp;:&nbsp;messages)&nbsp;</p><p>{&nbsp;&nbsp;&nbsp;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(message.delay&nbsp;&gt;&nbsp;3000)&nbsp;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;totalWaitTime&nbsp;+=&nbsp;message.delay;&nbsp;&nbsp;&nbsp;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</p><p>}

  然而随着Java 8的面世与大量新函数的出现,大家可以实现更为精致的代码结构,具体如下:

  文艺Java 8写法

long&nbsp;totWaitTime&nbsp;=&nbsp;messages.stream().filter(m&nbsp;-&gt;&nbsp;m.delay&nbsp;&gt;&nbsp;3000).mapToLong(m&nbsp;-&gt;&nbsp;m.delay).sum();

  请注意我将filter与mapToLong方法进行串连的方式,再加上一条terminal sum。顺便说一句,sum方法需要使用一种特殊的映射方法类型才能产生原始type集合,例如mapToLong以及mapToInt等等。

  函数式编程作为一大核心语言特性,能够为开发者带来令人叹为观止的强大构建能力。虽然大部分此类技术已经能够在各类第三方库(例如Guava)以及JVM语言(例如Scala与Groovy)中找到,但将这些关键特性融入Java仍然能够吸引更为广泛的开发者受众、并给未来的开发前景带来深远影响。

  毫无疑问,Java 8的出现让Java在通往完美的道路上再度迈出一大步。

来源URL:http://tech.it168.com/a2014/0604/1631/000001631263.shtml