天天看點

[Google Guava] 4-函數式程式設計

截至jdk7,java中也隻能通過笨拙冗長的匿名類來達到近似函數式程式設計的效果。預計jdk8中會有所改變,但guava現在就想給jdk5以上使用者提供這類支援。

過度使用guava函數式程式設計會導緻冗長、混亂、可讀性差而且低效的代碼。這是迄今為止最容易(也是最經常)被濫用的部分,如果你想通過函數式風格達成一行代碼,緻使這行代碼長到荒唐,guava團隊會淚流滿面。

比較如下代碼:

<code>01</code>

<code>function&lt;string, integer&gt; lengthfunction =</code><code>new</code> <code>function&lt;string, integer&gt;() {</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&lt;string&gt; allcaps =</code><code>new</code> <code>predicate&lt;string&gt;() {</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&lt;integer&gt; 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&lt;string&gt;() {</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&lt;string, integer&gt;() {</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&lt;integer&gt; 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&lt;a, b&gt;,它聲明了單個方法b apply(a input)。function對象通常被預期為引用透明的——沒有副作用——并且引用透明性中的”相等”語義與equals一緻,如a.equals(b)意味着function.apply(a).equals(function.apply(b))。

predicate&lt;t&gt;,它聲明了單個方法boolean apply(t input)。predicate對象通常也被預期為無副作用函數,并且”相等”語義與equals一緻。

此外,對可比較類型和基于比較邏輯的predicate,range類可以滿足大多數需求——它表示一個不可變區間。range類實作了predicate,用以判斷值是否在區間内。例如,range.atmost(2)就是個完全合法的predicate&lt;integer&gt;。更多使用range的細節請參照第8章。

<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/functions.html#formap(java.util.map)">formap(map&lt;a, b&gt;)</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&lt;b, c&gt;, function&lt;a, b&gt;)</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&lt;t&gt; 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&lt;string&gt; names;</code>

<code>map&lt;string, person&gt; personwithname;</code>

<code>list&lt;person&gt; people = lists.transform(names, functions.formap(personwithname));</code>

<code>listmultimap&lt;string, string&gt; firstnametolastnames;</code>

<code>// maps first names to all last names of people with that first name</code>

<code>listmultimap&lt;string, string&gt; firstnametoname = multimaps.transformentries(firstnametolastnames,</code>

<code>    </code><code>new</code> <code>entrytransformer&lt;string, string, string&gt; () {</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>