截至jdk7,java中也只能通过笨拙冗长的匿名类来达到近似函数式编程的效果。预计jdk8中会有所改变,但guava现在就想给jdk5以上用户提供这类支持。
过度使用guava函数式编程会导致冗长、混乱、可读性差而且低效的代码。这是迄今为止最容易(也是最经常)被滥用的部分,如果你想通过函数式风格达成一行代码,致使这行代码长到荒唐,guava团队会泪流满面。
比较如下代码:
<code>01</code>
<code>function<string, integer> lengthfunction =</code><code>new</code> <code>function<string, integer>() {</code>
<code>02</code>
<code> </code><code>public</code> <code>integer apply(string string) {</code>
<code>03</code>
<code> </code><code>return</code> <code>string.length();</code>
<code>04</code>
<code> </code><code>}</code>
<code>05</code>
<code>};</code>
<code>06</code>
<code>predicate<string> allcaps =</code><code>new</code> <code>predicate<string>() {</code>
<code>07</code>
<code> </code><code>public</code> <code>boolean</code> <code>apply(string string) {</code>
<code>08</code>
<code> </code><code>return</code> <code>charmatcher.java_upper_case.matchesallof(string);</code>
<code>09</code>
<code>10</code>
<code>11</code>
<code>multiset<integer> lengths = hashmultiset.create(</code>
<code>12</code>
<code> </code><code>iterables.transform(iterables.filter(strings, allcaps), lengthfunction));</code>
或fluentiterable的版本
<code> </code><code>fluentiterable.from(strings)</code>
<code> </code><code>.filter(</code><code>new</code> <code>predicate<string>() {</code>
<code> </code><code>public</code> <code>boolean</code> <code>apply(string string) {</code>
<code> </code><code>return</code> <code>charmatcher.java_upper_case.matchesallof(string);</code>
<code> </code><code>}</code>
<code> </code><code>})</code>
<code> </code><code>.transform(</code><code>new</code> <code>function<string, integer>() {</code>
<code> </code><code>public</code> <code>integer apply(string string) {</code>
<code> </code><code>return</code> <code>string.length();</code>
<code> </code><code>}));</code>
还有
<code>1</code>
<code>multiset<integer> lengths = hashmultiset.create();</code>
<code>2</code>
<code>for</code> <code>(string string : strings) {</code>
<code>3</code>
<code> </code><code>if</code> <code>(charmatcher.java_upper_case.matchesallof(string)) {</code>
<code>4</code>
<code> </code><code>lengths.add(string.length());</code>
<code>5</code>
<code>6</code>
<code>}</code>
即使用了静态导入,甚至把function和predicate的声明放到别的文件,第一种代码实现仍然不简洁,可读性差并且效率较低。
截至jdk7,命令式代码仍应是默认和第一选择。不应该随便使用函数式风格,除非你绝对确定以下两点之一:
使用函数式风格以后,整个工程的代码行会净减少。在上面的例子中,函数式版本用了11行, 命令式代码用了6行,把函数的定义放到另一个文件或常量中,并不能帮助减少总代码行。
为了提高效率,转换集合的结果需要懒视图,而不是明确计算过的集合。此外,确保你已经阅读和重读了effective java的第55条,并且除了阅读本章后面的说明,你还真正做了性能测试并且有测试数据来证明函数式版本更快。
请务必确保,当使用guava函数式的时候,用传统的命令式做同样的事情不会更具可读性。尝试把代码写下来,看看它是不是真的那么糟糕?会不会比你想尝试的极其笨拙的函数式 更具可读性。
guava提供两个基本的函数式接口:
function<a, b>,它声明了单个方法b apply(a input)。function对象通常被预期为引用透明的——没有副作用——并且引用透明性中的”相等”语义与equals一致,如a.equals(b)意味着function.apply(a).equals(function.apply(b))。
predicate<t>,它声明了单个方法boolean apply(t input)。predicate对象通常也被预期为无副作用函数,并且”相等”语义与equals一致。
此外,对可比较类型和基于比较逻辑的predicate,range类可以满足大多数需求——它表示一个不可变区间。range类实现了predicate,用以判断值是否在区间内。例如,range.atmost(2)就是个完全合法的predicate<integer>。更多使用range的细节请参照第8章。
<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/functions.html#formap(java.util.map)">formap(map<a, b>)</a>
<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/functions.html#compose(com.google.common.base.function,%20com.google.common.base.function)">compose(function<b, c>, function<a, b>)</a>
<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/functions.html#constant(e)">constant(t)</a>
<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/functions.html#identity()">identity()</a>
<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/functions.html#tostringfunction()">tostringfunction()</a>
细节请参考javadoc。
<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#instanceof(java.lang.class)">instanceof(class)</a>
<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#assignablefrom(java.lang.class)">assignablefrom(class)</a>
<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#contains(java.util.regex.pattern)">contains(pattern)</a>
<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#in(java.util.collection)">in(collection)</a>
<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#isnull()">isnull()</a>
<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#alwaysfalse()">alwaysfalse()</a>
<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#alwaystrue()">alwaystrue()</a>
<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#equalto(t)">equalto(object)</a>
<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#compose(com.google.common.base.predicate,%20com.google.common.base.function)">compose(predicate, function)</a>
<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#and(com.google.common.base.predicate...)">and(predicate...)</a>
<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#or(com.google.common.base.predicate...)">or(predicate...)</a>
<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/predicates.html#not(com.google.common.base.predicate)">not(predicate)</a>
guava提供了很多工具方法,以便用function或predicate操作集合。这些方法通常可以在集合工具类找到,如iterables,lists,sets,maps,multimaps等。
断言的最基本应用就是过滤集合。所有guava过滤方法都返回”视图”——译者注:即并非用一个新的集合表示过滤,而只是基于原集合的视图。
<b>集合类型</b><b></b>
<b>过滤方法</b><b></b>
iterable
iterator
collection
set
sortedset
map
sortedmap
multimap
*list的过滤视图被省略了,因为不能有效地支持类似get(int)的操作。请改用lists.newarraylist(collections2.filter(list, predicate))做拷贝过滤。
<b>iterables</b><b>方法签名</b><b></b>
<b>说明</b><b></b>
<b>另请参见</b><b></b>
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterables.html#all(java.lang.iterable,%20com.google.common.base.predicate)">boolean all(iterable, predicate)</a>
是否所有元素满足断言?懒实现:如果发现有元素不满足,不会继续迭代
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterables.html#any(java.lang.iterable,%20com.google.common.base.predicate)">boolean any(iterable, predicate)</a>
是否有任意元素满足元素满足断言?懒实现:只会迭代到发现满足的元素
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterables.html#find(java.lang.iterable,%20com.google.common.base.predicate)">t find(iterable, predicate)</a>
循环并返回<b>一个</b>满足元素满足断言的元素,如果没有则抛出nosuchelementexception
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterators.html#find(java.util.iterator,%20com.google.common.base.predicate)">iterators.find(iterator, predicate)</a>
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterables.html#find(java.lang.iterable,%20com.google.common.base.predicate,%20t)">iterables.find(iterable, predicate, t default)</a>
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterators.html#find(java.util.iterator,%20com.google.common.base.predicate,%20t)">iterators.find(iterator, predicate, t default)</a>
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterables.html#tryfind(java.lang.iterable,%20com.google.common.base.predicate)">optional<t> tryfind(iterable, predicate)</a>
返回<b>一个</b>满足元素满足断言的元素,若没有则返回optional.absent()
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterables.html#indexof(java.lang.iterable,%20com.google.common.base.predicate)">indexof(iterable, predicate)</a>
返回第一个满足元素满足断言的元素索引值,若没有返回-1
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterators.html#indexof(java.util.iterator,%20com.google.common.base.predicate)">iterators.indexof(iterator, predicate)</a>
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterables.html#removeif(java.lang.iterable,%20com.google.common.base.predicate)">removeif(iterable, predicate)</a>
移除所有满足元素满足断言的元素,实际调用iterator.remove()方法
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/iterators.html#removeif(java.util.iterator,%20com.google.common.base.predicate)">iterators.removeif(iterator, predicate)</a>
到目前为止,函数最常见的用途为转换集合。同样,所有的guava转换方法也返回原集合的视图。
<b>转换</b><b>方法</b><b></b>
list
map*
sortedmap*
multimap*
listmultimap*
table
*对set的转换操作被省略了,因为不能有效支持contains(object)操作——译者注:懒视图实际上不会全部计算转换后的set元素,因此不能高效地支持contains(object)。请改用sets.newhashset(collections2.transform(set, function))进行拷贝转换。
<code>list<string> names;</code>
<code>map<string, person> personwithname;</code>
<code>list<person> people = lists.transform(names, functions.formap(personwithname));</code>
<code>listmultimap<string, string> firstnametolastnames;</code>
<code>// maps first names to all last names of people with that first name</code>
<code>listmultimap<string, string> firstnametoname = multimaps.transformentries(firstnametolastnames,</code>
<code> </code><code>new</code> <code>entrytransformer<string, string, string> () {</code>
<code> </code><code>public</code> <code>string transformentry(string firstname, string lastname) {</code>
<code> </code><code>return</code> <code>firstname +</code><code>" "</code> <code>+ lastname;</code>
<code> </code><code>}</code>
<code>13</code>
<code> </code><code>});</code>
可以组合function使用的类包括:
ordering
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/ordering.html#onresultof(com.google.common.base.function)">ordering.onresultof(function)</a>
predicate
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/predicates.html#compose(com.google.common.base.predicate,%20com.google.common.base.function)">predicates.compose(predicate, function)</a>
equivalence
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/equivalence.html#onresultof(com.google.common.base.function)">equivalence.onresultof(function)</a>
supplier
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/suppliers.html#compose(com.google.common.base.function,%20com.google.common.base.supplier)">suppliers.compose(function, supplier)</a>
function
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/functions.html#compose(com.google.common.base.function,%20com.google.common.base.function)">functions.compose(function, function)</a>
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/futures.html#transform(com.google.common.util.concurrent.listenablefuture,%20com.google.common.base.function)">futures.transform(listenablefuture, function)</a>
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/futures.html#transform(com.google.common.util.concurrent.listenablefuture,%20com.google.common.base.function,%20java.util.concurrent.executor)">futures.transform(listenablefuture, function, executor)</a>
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/futures.html#transform(com.google.common.util.concurrent.listenablefuture,%20com.google.common.util.concurrent.asyncfunction)">futures.transform(listenablefuture, asyncfunction)</a>
<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/futures.html#transform(com.google.common.util.concurrent.listenablefuture,%20com.google.common.util.concurrent.asyncfunction,%20java.util.concurrent.executor)">futures.transform(listenablefuture, asyncfunction, executor)</a>