截至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>