带有谓词的Java中的功能样式-第1部分
什么是謂詞?
實際上,當(dāng)我很早以前在Java 1.4中進(jìn)行編碼時,我第一次發(fā)現(xiàn)Apache Commons Collections時就愛上了謂詞。 該API中的謂詞不過是Java界面,僅包含一種方法:
evaluate(Object object): boolean就是這樣,它只需要一些對象并返回true或false。 帶有Apache許可證2.0的Google Guava是Apache Commons Collections的更新版本。 它使用通用參數(shù)通過一種方法定義了謂詞接口:
apply(T input): boolean就這么簡單。 要在您的應(yīng)用程序中使用謂詞,您只需在自己的單個方法apply(something)中使用您自己的邏輯來實現(xiàn)此接口。 ?
一個簡單的例子
作為早期的示例,假設(shè)您有一個PurchaseOrder對象的列表訂單 ,每個訂單都有一個日期,一個Customer和一個州。 各種用例可能會要求您找出該客戶的每筆訂單,或者每筆待處理,已發(fā)貨或已交付的訂單,或者自上一小時以來完成的每筆訂單。 當(dāng)然,您可以通過foreach循環(huán)和if內(nèi)部循環(huán)來做到這一點:
//List<PurchaseOrder> orders...public List<PurchaseOrder> listOrdersByCustomer(Customer customer) {final List<PurchaseOrder> selection = new ArrayList<PurchaseOrder>();for (PurchaseOrder order : orders) {if (order.getCustomer().equals(customer)) {selection.add(order);}}return selection; }再次針對每種情況:
public List<PurchaseOrder> listRecentOrders(Date fromDate) {final List<PurchaseOrder> selection = new ArrayList<PurchaseOrder>();for (PurchaseOrder order : orders) {if (order.getDate().after(fromDate)) {selection.add(order);}}return selection; }重復(fù)非常明顯:除了if子句中的條件(此處以黑體強調(diào))之外,每個方法都是相同的。 使用謂詞的想法僅是通過對謂詞的調(diào)用來替換if子句中的硬編碼條件,該調(diào)用隨后成為參數(shù)。 這意味著您只能編寫一個方法,以謂詞作為參數(shù),并且仍然可以覆蓋所有用例,甚至已經(jīng)支持了您尚不知道的用例:
public List<PurchaseOrder> listOrders(Predicate<PurchaseOrder> condition ) {final List<PurchaseOrder> selection = new ArrayList<PurchaseOrder>();for (PurchaseOrder order : orders) {if (condition.apply(order)) {selection.add(order);}}return selection; }每個謂詞可以在多個地方定義為獨立類,也可以定義為匿名類:
final Customer customer = new Customer("BruceWaineCorp"); final Predicate<PurchaseOrder> condition = new Predicate<PurchaseOrder>() {public boolean apply(PurchaseOrder order) {return order.getCustomer().equals(customer);} };使用真正的函數(shù)式編程語言(Scala,Clojure,Haskell等)的朋友會評論說,上面的代碼非常冗長,無法完成某些非常常見的事情,我必須同意。 但是,我們已經(jīng)習(xí)慣了Java語法中的冗長性,并且我們擁有功能強大的工具(自動完成,重構(gòu))來適應(yīng)它。 而且我們的項目可能無法在一夜之間切換到另一種語法。 ?
謂詞是收藏最好的朋友
回到我們的示例,我們只編寫了一次foreach循環(huán)來覆蓋每個用例,并且我們對分解出來的內(nèi)容感到滿意。 但是,您的朋友“真正地”進(jìn)行函數(shù)式編程仍然可以嘲笑您必須自己編寫的循環(huán)。 幸運的是,來自Apache或Google的API也都提供了您可能期望的所有優(yōu)點,特別是類似于java.util.Collections的類,因此命名為Collections2 (不是一個非常原始的名稱)。
此類提供了一個方法filter() ,它的功能類似于我們之前編寫的內(nèi)容,因此我們現(xiàn)在可以完全不使用循環(huán)來重寫方法:
public Collection<PurchaseOrder> selectOrders(Predicate<PurchaseOrder> condition) {return Collections2.filter(orders, condition); }實際上,此方法返回一個篩選視圖:
返回的集合是unfiltered的實時視圖(輸入集合); 改變一個會影響另一個。
這也意味著更少的內(nèi)存使用,因為是從未經(jīng)過濾的 過濾 ,以實際返回的集合初始收集沒有實際的副本。
在類似的方法上,給定一個迭代器,您可以在它之上請求一個過濾的迭代器(Decorator模式),該迭代器僅為您提供謂詞選擇的元素:
Iterator filteredIterator = Iterators.filter(unfilteredIterator, condition);由于Java 5的Iterable接口在foreach循環(huán)中非常方便使用,因此我們確實希望使用以下表達(dá)式:
public Iterable<PurchaseOrder> selectOrders(Predicate<PurchaseOrder> condition) {return Iterables.filter(orders, condition); }// you can directly use it in a foreach loop, and it reads well: for (PurchaseOrder order : orders.selectOrders(condition)) {//... }現(xiàn)成的謂詞
要使用謂詞,您可以簡單地定義自己的接口謂詞,或者為應(yīng)用程序中需要的每個類型參數(shù)定義一個。 這是可能的,但是使用來自諸如Guava或Commons Collections之類的API的標(biāo)準(zhǔn)謂詞接口的好處是,API帶來了許多出色的構(gòu)建塊,可與您自己的謂詞實現(xiàn)結(jié)合使用。
首先,您甚至根本不需要實現(xiàn)自己的謂詞。 如果您所需要的只是一個對象是否等于另一個條件或不為空的條件,那么您可以簡單地要求謂詞:
// gives you a predicate that checks if an integer is zero Predicate<Integer> isZero = Predicates.equalTo(0); // gives a predicate that checks for non null objects Predicate<String> isNotNull = Predicates.notNull(); // gives a predicate that checks for objects that are instanceof the given Class Predicate<Object> isString = Predicates.instanceOf(String.class);給定一個謂詞,您可以將其求逆(true變?yōu)閒alse,反之亦然):
Predicates.not(predicate);使用布爾運算符AND或OR組合多個謂詞:
Predicates.and(predicate1, predicate2); Predicates.or(predicate1, predicate2); // gives you a predicate that checks for either zero or null Predicate<Integer> isNullOrZero = Predicates.or(isZero, Predicates.isNull());當(dāng)然,您還有特殊的謂詞,它們總是返回true或false,它們確實非常有用,我們將在以后的測試中看到:
Predicates.alwaysTrue(); Predicates.alwaysFalse();謂詞在哪里
我通常經(jīng)常一開始會做匿名謂語,但是它們總是經(jīng)常被使用,因此經(jīng)常被提升為實際的類,無論是否嵌套。
順便說一下,這些謂詞在哪里定位? 遵循羅伯特·C·馬丁( Robert C. Martin) 及其共同封閉原則(CCP) :
一起變化的類,一起屬于
因為謂詞操縱某種類型的對象,所以我喜歡將它們共置為靠近它們作為參數(shù)的類型。 例如,類CustomerOrderPredicate , PendingOrderPredicate和RecentOrderPredicate應(yīng)該與它們評估的PurchaseOrder類駐留在同一包中,或者如果它們很多,則駐留在子包中。 另一種選擇是定義它們嵌套在類型本身內(nèi)。 顯然,謂詞與它們所操作的對象非常相關(guān)。
資源資源
以下是本文示例的源文件: cyriux_predicates_part1 (zip)
在下一部分中 ,我們將了解謂詞如何簡化測試,它們與域驅(qū)動設(shè)計中的規(guī)范之間的關(guān)系以及一些其他方面的知識,以使您的謂詞發(fā)揮最大作用。
參考: 帶有謂詞的純Java語言中的功能樣式 -Cyrille Martraire博客博客中來自JCG合作伙伴 Cyrille Martraire的第1部分 。
翻譯自: https://www.javacodegeeks.com/2012/05/functional-style-in-java-with.html
總結(jié)
以上是生活随笔為你收集整理的带有谓词的Java中的功能样式-第1部分的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑硬盘发现木马(查杀硬盘中的木马程序)
- 下一篇: 生化狂潮1.24电脑模式(生化狂潮1.2