http://openhome.cc/Gossip/Java/ConsumerFunctionPredicateSupplier.html
Lambda表示式實際的型態要看函式介面,雖然可以自行定義所需的函式介面,只不過對於幾種函式介面的行為,JDK8已經定義了幾個通用的函式介面,你可以先基於這些通用函式介面來撰寫程式,在必要時再考慮自訂函式介面,JDK8定義的通用函式介面,基本上置放於java.util.function
套件之中,就行為來說,基本上可以分為Consumer
、Function
、Predicate
與Supplier
四種。
Consumer
如果需要的行為是接受一個引數,然後處理後不傳回值,就可以使用Consumer
介面,它的定義是:
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Consumer
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 {
R apply(T t);
...
}
Function
,應用的例子之一,像是 使用
Optional
取代
null
中的
Optional
實例有個
map()
方法,就接受
Function
實例,如果
Optional
有包含值,那就會用指定的
Function
來取得值進行結果計算,如果結果不為
null
,就建立
Optional
實例來包裹結果並傳回,如果結果為
null
,或者是一開始的
Optional
沒有值,就傳回不包括值的
Optional
實例。例如:
Optional nickOptional = getNickName("Justin");
out.println(nickOptional.map(String::toUpperCase)); // 顯示 CATERPILLAR
Function
的子介面為
UnaryOperator
,特化為參數與傳回值都是相同型態(雖然JDK8仍不支援函數式語言中的運算子重載,不過這個命名顯然源自於函數式語言中,運算子也是個函數的概念):
@FunctionalInterface
public interface UnaryOperator extends Function
IntFunction
、
LongFunction
、
DoubleFunction
、
IntToDoubleFunction
、
IntToLongFunction
、
LongToDoubleFunction
、
LongToIntFunction
、
DoubleToIntFunction
、
DoubleToLongFunction
等函式介面,看看它們的名稱或API文件,作用應該都一目瞭然。
BiFunction
:
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface BiFunction {
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 {
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 get();
}
static void randomZero(Integer[] coins, Supplier randomSupplier) {
coins[randomSupplier.get()] = 0;
}
Integer[] coins = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10};
randomZero(coins, () -> (int) (Math.random() * 10));
Stream
有個
generate()
方法,可以這麼使用:
Stream decimalNumbersOfPI = Stream.generate(() -> nextDecimalNumberOfPI());
decimalNumbersOfPI.map(n -> n + 10)
.filter(n -> n < 15)
.forEach(out::println);
Stream
的原因是可以像個清單似地操作,而實際上在
forEach()
真正消耗某個數字之前,並不會真正去呼叫
nextDecimalNumberOfPI()
,消耗掉的數字也不會被保留,因而不會耗費記憶體,因而可以實現無限長度清單的概念。
BooleanSupplier
、
DoubleSupplier
、
IntSupplier
、
LongSupplier
,應該不用解釋了,真不知道就直接查詢一下API