【Guava 教學】(10)進行函數式程式設計 by caterpillar | CodeData
top

【Guava 教學】(10)進行函數式程式設計

分享:

【Guava 教學】(9)ListenableFuture 聽取未來需求 << 前情

由於 JDK8 Lambda 的引進,未來 JDK8 將有進行函數式程式設計的可能性,剛好本站 Java 開發者的函數式程式設計系列 已經告一段落,閱讀該系列的文章,將有助於將來在 JDK8 中,善用那些從函數式程式設計借鏡而來的相關設計,無論是 Lambda 語法或程式庫的使用。

那麼在 JDK7 前的版本呢?如果想在 Java 中進行函數式風格的設計有可能嗎?其實有一些第三方程式庫提供了這類風格的封裝,其中包括了 Guava 程式庫,然而缺少 Lambda 語法的輔助,使用 Guava 程式庫的相關 API 並不一定會因為函數式風格而受益,正如 Guava 的 FunctionalExplained 中提到的,除非使用這些 API 進行函數式風格設計時,對可讀性有所幫助,或者是取得了 惰性 處理上的一些益處,不然命令式仍應是 Java 的風格選擇。

雖然 Guava 的 FunctionalExplained 中也提到,其函數式風格的 API 主要是針對 JDK5 到 JDK7 的使用者,不過使用 JDK8 的 Lambda 語法來搭配 Guava 的函數式風格 API,似乎也是不錯的選擇,特別是你要與 Guava 的其他 API 做溝通,或甚至你覺得 JDK8 的 API 設計得有些醜陋時。

Guava 的 Iterables 與 Iterators 提到了一些函數式風格的函式,呃!Java 中是叫做靜態方法啦!總之,Java 中的靜態方法,某些程度上就只是將類別名稱當做名稱空間,就稱它們是函式吧!Iterables 上的函式針對有 Iterable 行為的物件,而 Iterators 是針對實作 Iterator 介面的物件,如此而已,因而如果你有個 List<String> 的名稱清單,想要過濾出長度小於 5 的名稱,可以在 import static com.google.common.collect.Iterables.* 後,直接進行如下的風格撰寫:

Iterable<String> filteredNames = filter(names, name -> name.length() < 5);

看到傳回值是 Iterable 型態,你應該要意識到這可能實現了惰性,在實際迭代傳回的物件前,傳給 filter 的 Predicateapply 並不會被執行。Java 開發者的函數式程式設計系列 中提到的 map 函式,在 Guava 中對應的是 transform 方法,例如取得清單所有名稱之長度清單,可以如下:

Iterable lengthes = transform(names, name -> name.length());

Guava 不提供 reduce 這類功能的函式,這應該是基於可讀性的關係,因為 reduce 這個名稱(或像是 foldfoldLeftfoldRight 等)本身並不容易讓人瞭解它的意涵,不過對於一些可用 reduce 做到的功能,但有更明確目的且常用的函式,Guava 則提到有 allany 等函式。

Predicates 上也提到了一些函式,例如,若你有兩個 Predicates,像是 name -> name.length() < 5 而且 name -> name.startWith("Java"),就可以用 Predicates 上的 and 來組合。例如:

Iterable<String> filteredNames = filter(
    names,
    and(name -> name.length() < 5, name -> name.startsWith("Java"))
);

那麼,如果想進行鏈狀操作呢?畢竟上面的寫法,比較像是 Java 開發者的函數式程式設計(5) 提到的,filtertransform 那些函式,比較像是二級公民,感覺不符合 Java 物件導向的主流典範,如果想過濾出清單中名稱長度小於 5,接著取得那些名稱的大寫,然後看看有沒有任何一個名稱是包括 “JAVA" 的,用上面的寫法會變成:

boolean anyJAVA = any(transform(filter(names, name -> name.length() < 5), name -> name.toUpperCase()), name -> name.equals("JAVA"));

幾乎沒什麼可讀性,你可以改用 FluentIterable 來進行操作,FluentIterable 上有個 from 方法,可以直接將你的 Iterable 包裹,傳回 FluentIterable 實例,接著你就可以進行以下風格的操作:

boolean anyJAVA = FluentIterable.from(names)
             .filter(name -> name.length() < 5)
             .transform(name -> name.toUpperCase())
             .anyMatch(name -> name.equals("JAVA"));

曾經聽其他人說過,看不懂 IterablesIterators 上那些方法怎麼使用,我想可能的原因有兩個,一是因為 JDK8 前沒有 Lambda,只能使用醜醜的匿名類別,不知道用這些方法有什麼好處,二是不清楚函數式風格與由來,不知道怎麼搭配那些 API 來取得想要的結果,如果你瞭解了函數式設計,也擁有了 Lambda,那麼應該就知道怎麼善用它們了吧!看看本站的兩個系列:

加上這篇文章,相信再去看 Guava 的 FunctionalExplained,就可以知道它上頭在說些什麼了。

 

分享:
按讚!加入 CodeData Facebook 粉絲群

相關文章

留言

留言請先。還沒帳號註冊也可以使用FacebookGoogle+登錄留言

Valentino Young02/20

Guava的FunctionalExplained, 跟函數式設計其實是同曲異工.
標題之所以用 explained 而非 programming,
表示代表著 "用函數風格詮釋代碼意圖" 而非全然的 "函數式程式設計".

表示看不懂Iterables上那些方法怎麼使用的, 並非缺乏對函數式設計的瞭解或便捷的語法之所致,
就跟過去覺得用if/for/swithc/while巢狀迴圈才是簡單的直覺的容易看懂的,
不瞭解以CollectionUtils(Iterables懷舊版)為重心的設計的好處在哪裡...的原因相近,
就是其"程式設計師"的頭銜不符"設計師"之實的老問題而已.

若以函數式設計為出發點,
注意力會集中在apache的CollectionUtils, Guava的Iterables, JDK8的Stream等等的主引擎,
然而以一個又一個的系統建置用來服務數十數百個縱向業務邏輯,
然後橫斷式需求的變更又不斷與時俱進, 或朝秦暮楚...的經歷而言,
AndPredicate/AllPredicate(Guava:AndPredicate)
,ChainedTransformer(Guava:FunctionComposition)
,PredicateTransfomer(Guava:PredicateFunction)
,TransfomedPredicate(Guava:CompositionPredicate)
,NOPClosure,TransformerClosure,ExceptionClosure...
...諸如此類被引擎運轉帶動的輔助器械的活用才是重點.

雖然已經有些年歲, 不過org.apache.commons.collections.functors仍舊值得一看,
除了翻遍主引擎的JavaDoc跟原始碼意圖將其運用至極限之外,
多用輔助器械而讓主引擎簡單運轉即可一樣能達到相同目的...的角度, 也是值得考慮的.

Guava在cleancoder的表現當然是比2002年就出現的apache collection成熟許多,
唯獨個人覺得把Transformer變成Function並不是那麼地適當, 那是函數式程式設計風潮使然...
以FunctionalExplained為題而非functional programming,
CollectionUtils的transform方法到了Iterables上仍舊名為transform,
...暗示著與函數式程式設計雖然調性相符但本質仍舊是不同的實情.

熱門論壇文章

熱門技術文章