Java8 java.util.function包解析(Function,Consumer,Predicate,Supplier)
Java8的工具包新增了function包,裡面主要包含了Function,Consumer,Predicate,Supplier四種類。
一、Function類
Function類包含四種方法,其中一個抽象方法apply(),兩個default方法andThen()和compose(),以及一個靜態方法identity()。
例項化Function的時候需要實現其中的apply()方法,apply方法接收一個模板型別作為輸入引數,在andThen()和compose()方法裡會自動呼叫apply()方法。
andThen方法接收一個Function類的例項,通過andThen可以將任意多個Function的apply方法呼叫連線起來,在Java8裡的原始碼如下:
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
測試用例:
Function<String, String> function1 = string -> {
System.out.println("function1輸出了: " + string );
return string;
};
Function<String, String> function2 = string -> {
System.out.println("function2輸出了: " + string);
return string;
};
function1.andThen(function2).apply("hello world");
輸出結果
function1輸出了: hello world
function2輸出了: hello world
可以看到呼叫順序是先呼叫funtion1然後呼叫function2。
compose方法和andThen方法一樣接收一個另一個Function作為引數,但是順序與andThen恰恰相反。
在Java8裡的原始碼如下:
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
接下來是測試用例,保持前面的不變,只把最後一句由andThen改成
function1.compose(function2).apply("hello world");
輸出結果:
function2輸出了: hello world
function1輸出了: hello world
可以看到這次先執行了function2之後再執行function1。
identity方法是一個靜態方法,作用是返回一個Function物件,返回的物件總是返回它被傳入的值。
原始碼很簡單
static <T> Function<T, T> identity() {
return t -> t;
}
二、Consumer類
Consumer類包含兩個方法,一個accept方法用來對輸入的引數進行自定義操作,因為是個抽象方法,所以需要例項化物件的時候進行Override,另一個andThen方法跟Function的方法一樣是一個default方法,已經有內部實現所以不需要使用者重寫,並且具體功能也跟Function差不多。Consumer的中文意思是消費者,意即通過傳遞進一個引數來對引數進行操作。
示例程式碼:
import java.util.function.Consumer;
public class Test {
public static void main(String[] args) {
Foo f = new Foo();
f.foo(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
}
}
class Foo {
private int[] data = new int[10];
public Foo() {
for(int i = 0; i < 10; i++) {
data[i] = i;
}
}
public void foo(Consumer<Integer> consumer) {
for(int i : data)
consumer.accept(i);
}
}
首先新建一個類,在這個類裡有一個int型陣列,在構造方法裡對陣列賦初值,然後又一個foo方法傳入一個Consumer物件,對每一個數組項呼叫consumer物件的accept方法。在main函式裡實例化Foo物件並呼叫foo方法。
輸出結果:
0
1
2
3
4
5
6
7
8
9
在上面的程式碼中,由於Java8引入的lambda表示式,所以其中的
f.foo(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
可以簡寫成
f.foo(integer -> System.out.println(integer));
或者進一步簡寫成
f.foo(System.out::println);
三、Predicate類
Predicate類包含5個方法,最重要的是test方法,這是一個抽象方法,需要程式設計者自己去Override,其他的三個default方法裡都使用到了這個方法,這三個方法分別是and方法,negate方法和or方法,其中and和or方法與前面兩個類的andThen方法類似,這兩個方法都接受另一個Predicate物件作為引數,and方法返回這兩個物件分別呼叫test方法之後得到的布林值的並,相當於predicate1.test() && predicate2.test(),or方法返回這兩個物件分別呼叫test方法之後得到的布林值的或,相當於predicate1.test() || predicate2.test()。
示例程式碼:
Predicate<Integer> predicate1 = new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
return integer <= 0;
}
};
Predicate<Integer> predicate2 = new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
return integer > 0;
}
};
System.out.println("and: " + predicate1.and(predicate2).test(1));
System.out.println("or: " + predicate1.or(predicate2).test(1));
System.out.println("negate: " + predicate1.negate().test(1));
輸出結果:
and: false
or: true
negate: true
同樣,可以簡化成lambda表示式
Predicate<Integer> predicate1 = integer -> integer <= 0;
Predicate<Integer> predicate2 = integer -> integer > 0;
四、Supplier類
supplier的中文意思是提供者,跟Consumer類相反,Supplier類用於提供物件,它只有一個get方法,是一個抽象方法,需要程式設計者自定義想要返回的物件。
示例程式碼:
Supplier<Integer> supplier = new Supplier<Integer>() {
@Override
public Integer get() {
return new Random().nextInt(100);
}
};
int[] ints = new int[10];
for(int i = 0; i < 10; i++) {
ints[i] = supplier.get();
}
Arrays.stream(ints).forEach(System.out::println);
首先自定義了一個Supplier物件,對於其get方法,我每次都返回一個100以內的隨機數,並在之後利用這個物件給一個長度為10的int陣列賦值並輸出。
輸出結果:
49
27
14
46
8
97
20
82
75
30
總結:
util裡的function包裡並不僅僅只有這四個類,只是其中絕大部分都是由這四種衍生而來的,這個包主要是用於實現Java8最大的特性函數語言程式設計,所以在很多其他的包的一些類中的很多地方都用到了這個function包裡的類,更多的情況是作為方法中的一個匿名物件引數來使用,配合簡潔的lambda表示式使得程式可讀性變得更好。
最近正在學習Java的API,看到網上跟function包相關的知識比較少,所以自己通過了一天的嘗試了之後記錄總結了一下,肯定還有很多錯誤,畢竟接觸Java的時間不長,以後還會回來修改完善。