和Lambda表达式相关的Consumer、Function、Predicate 与 Supplier
2016-11-07 16:39
323 查看
http://openhome.cc/Gossip/Java/ConsumerFunctionPredicateSupplier.html
Lambda表示式實際的型態要看函式介面,雖然可以自行定義所需的函式介面,只不過對於幾種函式介面的行為,JDK8已經定義了幾個通用的函式介面,你可以先基於這些通用函式介面來撰寫程式,在必要時再考慮自訂函式介面,JDK8定義的通用函式介面,基本上置放於
如果需要的行為是接受一個引數,然後處理後不傳回值,就可以使用
接受
既然接受了引數但沒有傳回值,這行為就像純綷消耗了引數,也就是命名為
如果需要的是接受一個引數,然後以該引數進行計算後傳回結果,就可以使用
因為這行為就像是數學函數y=f(x),給予x值計算出y值的概念,因此命名為
使用
對於基本型態,則有著
如果需要接受兩個引數而後傳回一個結果,則可以使用
類似地,
如果接受一個引數,然後只傳回
舉例來說,如果有個檔案名稱的
之後還會詳細介紹
如果需要的行為是不接受任何引數,然後傳回值,那可以使用
既然不接受引數,就能傳回值,傳回值的來源就有幾個可能性,像是固定值、某個時間某個事物的狀態值、某個外部輸入值、某個要按需(On-demand)索取的(昂貴)運算等。舉例來說,也許你的某個方法需要產生亂數,而你需要不同的亂數產生方式,那可以設計為:
那麼就可以如此使用:
來看個更實際的應用之一,想想看,怎麼產生一個無限長度的數字清單呢?例如,PI的小數是無限的,如果有個演算需要逐一走訪這些小數,不知道何時會停止,那該怎麼辦?之後會介紹到的
使用
至於那些
Lambda表示式實際的型態要看函式介面,雖然可以自行定義所需的函式介面,只不過對於幾種函式介面的行為,JDK8已經定義了幾個通用的函式介面,你可以先基於這些通用函式介面來撰寫程式,在必要時再考慮自訂函式介面,JDK8定義的通用函式介面,基本上置放於
java.util.function套件之中,就行為來說,基本上可以分為
Consumer、
Function、
Predicate與
Supplier四種。
Consumer
如果需要的行為是接受一個引數,然後處理後不傳回值,就可以使用
Consumer介面,它的定義是:
package java.util.function; import java.util.Objects; @FunctionalInterface public interface Consumer<T> { void accept(T t); ... }
接受
Consumer的實際例子就是
Iterable上的
forEach方法:
default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } }
既然接受了引數但沒有傳回值,這行為就像純綷消耗了引數,也就是命名為
Consumer的原因,如果產出,就是以副作用(Side effect)形式呈現,像是改變某物件狀態,或者是進行了輸入輸出,例如,使用
System.out的
println()進行輸出:
Arrays.asList("Justin", "Monica", "Irene").forEach(out::println);
Consumer介面主要是接受單一物件實例作為引數,對於基本型態
int、
long、
double,另外有
IntConsumer、
LongConsumer、
DoubleConsumer三個函式介面;另外還有
ObjIntConsumer、
ObjLongConsumer、
ObjDoubleConsumer,這三個函式介面第一個參數接受物件實例,第二個參數接受的基本型態則對應至類別名稱。
Function
如果需要的是接受一個引數,然後以該引數進行計算後傳回結果,就可以使用
Function介面,它的定義是:
package java.util.function; import java.util.Objects; @FunctionalInterface public interface Function<T, R> { R apply(T t); ... }
因為這行為就像是數學函數y=f(x),給予x值計算出y值的概念,因此命名為
Function,應用的例子之一,像是
使用
Optional取代
null中的
Optional實例有個
map()方法,就接受
Function實例,如果
Optional有包含值,那就會用指定的
Function來取得值進行結果計算,如果結果不為
null,就建立
Optional實例來包裹結果並傳回,如果結果為
null,或者是一開始的
Optional沒有值,就傳回不包括值的
Optional實例。例如:
Optional<String> nickOptional = getNickName("Justin");
out.println(nickOptional.map(String::toUpperCase)); // 顯示 CATERPILLAR
Function的子介面為
UnaryOperator,特化為參數與傳回值都是相同型態(雖然JDK8仍不支援函數式語言中的運算子重載,不過這個命名顯然源自於函數式語言中,運算子也是個函數的概念):
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T,T>
對於基本型態,則有著
IntFunction、
LongFunction、
DoubleFunction、
IntToDoubleFunction、
IntToLongFunction、
LongToDoubleFunction、
LongToIntFunction、
DoubleToIntFunction、
DoubleToLongFunction等函式介面,看看它們的名稱或API文件,作用應該都一目瞭然。
如果需要接受兩個引數而後傳回一個結果,則可以使用
BiFunction:
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
...
}
類似地,
BinaryOperator是
BiFunction的子介面,特化為兩個參數與傳回值都是相同型態,對於基本型態,也有一些對應的函式介面,只要是BiFunction或是BinaryOperator名稱結尾的,都是類似的東西,可以直接查詢API來瞭解。
Predicate
如果接受一個引數,然後只傳回
boolean值,也就是根據傳入的引數直接論斷真假的行為,就可以使用
Predicate函式介面,其定義為:
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
...
}
舉例來說,如果有個檔案名稱的
String陣列
fileNames,想要知道其中副檔名為.txt的有幾個,可以如下:
long count = Stream.of(fileNames) .filter(name -> name.endsWith("txt")) .count();
之後還會詳細介紹
Stream,此實例的
filter()方法接受
Predicate實例,每個元素都會由
Predicate來判斷是否被過濾出來保留。類似地,
BiPredicate是接受兩個引數,傳回
boolean值,基本型態對應的函式介面,則有
IntPredicate、
LongPredicate、
DoublePredicate。
Supplier
如果需要的行為是不接受任何引數,然後傳回值,那可以使用
Supplier函式介面:
package java.util.function;
@FunctionalInterface
public interface Supplier<T> {
T get();
}
既然不接受引數,就能傳回值,傳回值的來源就有幾個可能性,像是固定值、某個時間某個事物的狀態值、某個外部輸入值、某個要按需(On-demand)索取的(昂貴)運算等。舉例來說,也許你的某個方法需要產生亂數,而你需要不同的亂數產生方式,那可以設計為:
static void randomZero(Integer[] coins, Supplier<Integer> randomSupplier) {
coins[randomSupplier.get()] = 0;
}
那麼就可以如此使用:
Integer[] coins = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10};
randomZero(coins, () -> (int) (Math.random() * 10));
來看個更實際的應用之一,想想看,怎麼產生一個無限長度的數字清單呢?例如,PI的小數是無限的,如果有個演算需要逐一走訪這些小數,不知道何時會停止,那該怎麼辦?之後會介紹到的
Stream有個
generate()方法,可以這麼使用:
Stream<Integer> decimalNumbersOfPI = Stream.generate(() -> nextDecimalNumberOfPI());
decimalNumbersOfPI.map(n -> n + 10) .filter(n -> n < 15) .forEach(out::println);
使用
Stream的原因是可以像個清單似地操作,而實際上在
forEach()真正消耗某個數字之前,並不會真正去呼叫
nextDecimalNumberOfPI(),消耗掉的數字也不會被保留,因而不會耗費記憶體,因而可以實現無限長度清單的概念。
至於那些
BooleanSupplier、
DoubleSupplier、
IntSupplier、
LongSupplier,應該不用解釋了,真不知道就直接查詢一下API
相关文章推荐
- Java8的学习计划--lambda表达式的Function_predicate_consumer_bifunction
- 【java】【java8】Lambda、Stream、Function、Consumer、Predicate、Supplier
- 学习笔记5:java 1.8 Predicate,Function,Supplier,Consumer,Comparator,Optional,Stream接口的使用
- Java 8中,Function,Consumer,Predicate,Supplier举例
- Java 8中,Function,Consumer,Predicate,Supplier举例 ,以及CompletableFuture使用
- java的lambda表达式、匿名类,Predicate接口,Consumer接口的应用
- C++ 11: function,bind和lambda表达式
- java8 Function,Consumer,Predicate 接口
- Lambda 表达式 和 Predicate 泛型委托
- Java JVM(七):Function,Consumer,Predicate 接口
- guava Function Predicate Supplier
- java8函数式接口——Precidate、Consumer、Function、Supplier
- lambda表达式相关
- lambda表达式与Function接口
- JAVA8 Function、Consumer、Predicate、Supplier接口
- guava Function Predicate Supplier
- lambda表达式与Function接口
- delegate event lambda Function Action Predicate
- lambda表达式与Function接口
- Java SE8 Lambda 基础入门---两个函数式接口:Predicate、Consumer