截至 JDK7,Java 中也只能通過笨拙冗長的匿名類來達到近似函數(shù)式編程的效果。預(yù)計 JDK8 中會有所改變,但 Guava 現(xiàn)在就想給 JDK5 以上用戶提供這類支持。
過度使用 Guava 函數(shù)式編程會導(dǎo)致冗長、混亂、可讀性差而且低效的代碼。這是迄今為止最容易(也是最經(jīng)常)被濫用的部分,如果你想通過函數(shù)式風(fēng)格達成一行代碼,致使這行代碼長到荒唐,Guava 團隊會淚流滿面。
比較如下代碼:
Function<String, Integer> lengthFunction = new Function<String, Integer>() {
public Integer apply(String string) {
return string.length();
}
};
Predicate<String> allCaps = new Predicate<String>() {
public boolean apply(String string) {
return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string);
}
};
Multiset<Integer> lengths = HashMultiset.create(
Iterables.transform(Iterables.filter(strings, allCaps), lengthFunction));
或 FluentIterable 的版本
Multiset<Integer> lengths = HashMultiset.create(
FluentIterable.from(strings)
.filter(new Predicate<String>() {
public boolean apply(String string) {
return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string);
}
})
.transform(new Function<String, Integer>() {
public Integer apply(String string) {
return string.length();
}
}));
還有
Multiset<Integer> lengths = HashMultiset.create();
for (String string : strings) {
if (CharMatcher.JAVA_UPPER_CASE.matchesAllOf(string)) {
lengths.add(string.length());
}
}
即使用了靜態(tài)導(dǎo)入,甚至把 Function 和 Predicate 的聲明放到別的文件,第一種代碼實現(xiàn)仍然不簡潔,可讀性差并且效率較低。
截至 JDK7,命令式代碼仍應(yīng)是默認和第一選擇。不應(yīng)該隨便使用函數(shù)式風(fēng)格,除非你絕對確定以下兩點之一:
請務(wù)必確保,當使用 Guava 函數(shù)式的時候,用傳統(tǒng)的命令式做同樣的事情不會更具可讀性。嘗試把代碼寫下來,看看它是不是真的那么糟糕?會不會比你想嘗試的極其笨拙的函數(shù)式 更具可讀性。
本節(jié)只討論直接與 Function 和 Predicate 打交道的 Guava 功能。一些其他工具類也和”函數(shù)式風(fēng)格”相關(guān),例如 Iterables.concat(Iterable
Guava 提供兩個基本的函數(shù)式接口:
字符類型有自己特定版本的 Predicate——CharMatcher,它通常更高效,并且在某些需求方面更有用。CharMatcher 實現(xiàn)了 Predicate
此外,對可比較類型和基于比較邏輯的 Predicate,Range 類可以滿足大多數(shù)需求——它表示一個不可變區(qū)間。Range 類實現(xiàn)了 Predicate,用以判斷值是否在區(qū)間內(nèi)。例如,Range.atMost(2)就是個完全合法的 Predicate
Functions 提供簡便的 Function 構(gòu)造和操作方法,包括:
forMap(Map<A, B>) | compose(Function<B, C>, Function<A, B>) | constant(T) |
identity() | toStringFunction() |
細節(jié)請參考 Javadoc。
相應(yīng)地,Predicates 提供了更多構(gòu)造和處理 Predicate 的方法,下面是一些例子:
細節(jié)請參考Javadoc。
Guava 提供了很多工具方法,以便用 Function 或 Predicate 操作集合。這些方法通??梢栽诩瞎ぞ哳愓业?,如 Iterables,Lists,Sets,Maps,Multimaps 等。
斷言的最基本應(yīng)用就是過濾集合。所有 Guava 過濾方法都返回”視圖”——譯者注:即并非用一個新的集合表示過濾,而只是基于原集合的視圖。
*List 的過濾視圖被省略了,因為不能有效地支持類似 get(int)的操作。請改用 Lists.newArrayList(Collections2.filter(list, predicate))做拷貝過濾。
除了簡單過濾,Guava 另外提供了若干用 Predicate 處理 Iterable 的工具——通常在 Iterables 工具類中,或者是 FluentIterable 的”fluent”(鏈式調(diào)用)方法。
Iterables方法簽名 | 說明 | 另請參見 |
boolean all(Iterable, Predicate) | 是否所有元素滿足斷言?懶實現(xiàn):如果發(fā)現(xiàn)有元素不滿足,不會繼續(xù)迭代 | Iterators.all(Iterator, Predicate)FluentIterable.allMatch(Predicate) |
boolean any(Iterable, Predicate) | 是否有任意元素滿足元素滿足斷言?懶實現(xiàn):只會迭代到發(fā)現(xiàn)滿足的元素 | Iterators.any(Iterator, Predicate)FluentIterable.anyMatch(Predicate) |
T find(Iterable, Predicate) | 循環(huán)并返回一個滿足元素滿足斷言的元素,如果沒有則拋出 NoSuchElementException |
Iterators.find(Iterator, Predicate) Iterables.find(Iterable, Predicate, T default) Iterators.find(Iterator, Predicate, T default) |
Optional<T> tryFind(Iterable, Predicate) | 返回一個滿足元素滿足斷言的元素,若沒有則返回Optional.absent() |
Iterators.find(Iterator, Predicate) Iterables.find(Iterable, Predicate, T default) Iterators.find(Iterator, Predicate, T default) |
indexOf(Iterable, Predicate) | 返回第一個滿足元素滿足斷言的元素索引值,若沒有返回-1 | Iterators.indexOf(Iterator, Predicate) |
removeIf(Iterable, Predicate) | 移除所有滿足元素滿足斷言的元素,實際調(diào)用Iterator.remove()方法 | Iterators.removeIf(Iterator, Predicate) |
到目前為止,函數(shù)最常見的用途為轉(zhuǎn)換集合。同樣,所有的 Guava 轉(zhuǎn)換方法也返回原集合的視圖。
*Map 和 Multimap 有特殊的方法,其中有個 EntryTransformer<K, V1, V2>參數(shù),它可以使用舊的鍵值來計算,并且用計算結(jié)果替換舊值。
對 Set 的轉(zhuǎn)換操作被省略了,因為不能有效支持 contains(Object)操作——譯者注:懶視圖實際上不會全部計算轉(zhuǎn)換后的 Set 元素,因此不能高效地支持 contains(Object)。*請改用 Sets.newHashSet(Collections2.transform(set, function))進行拷貝轉(zhuǎn)換。
List<String> names;
Map<String, Person> personWithName;
List<Person> people = Lists.transform(names, Functions.forMap(personWithName));
ListMultimap<String, String> firstNameToLastNames;
// maps first names to all last names of people with that first name
ListMultimap<String, String> firstNameToName = Multimaps.transformEntries(firstNameToLastNames,
new EntryTransformer<String, String, String> () {
public String transformEntry(String firstName, String lastName) {
return firstName + " " + lastName;
}
});
可以組合 Function 使用的類包括:
Ordering | Ordering.onResultOf(Function) |
Predicate | Predicates.compose(Predicate, Function) |
Equivalence | Equivalence.onResultOf(Function) |
Supplier | Suppliers.compose(Function, Supplier) |
Function | Functions.compose(Function, Function) |
此外,ListenableFuture API 支持轉(zhuǎn)換 ListenableFuture。Futures 也提供了接受 AsyncFunction 參數(shù)的方法。
AsyncFunction 是 Function 的變種,它允許異步計算值。