stream and lambda(3) - jdk 提供的函式式介面
使用函式式介面開發,如果針對每個需要使用的場景都自定義一個函式式介面,會導致我們的專案中有很多僅僅當引數的介面。如果我們能自己抽離這些使用場景的共性,則會省去很多自定義函式式介面,相信很多人會遇到這種情況。幸運的是,JDK 已經提前幫我們把這一步做好了。
Java8 之前的函式式介面
Java8 之前是沒有函式式介面這個概念的,但是在 Java8 中,有些介面升級成了函式式介面。最常見的當屬以下三位選手:
1 . java.lang.Runnable 2 . java.util.concurrent.Callable 3 . java.util.Comparator
Java8 之後的函式式介面
Java8 提供了一系列的函式式介面,主要在 java.util.function
包目錄下。在瞭解這些函式式介面前,再默唸幾遍之前反覆提到的要點: 介面名不重要(反正也是匿名),方法名不重要(反正只有一個抽象方法),重要的是能幹什麼(行為)
。
介面 | 入參 | 返回型別 | 說明 |
---|---|---|---|
Function<T,R> | T | R | 輸入 T 返回 R,有輸入有輸出 |
Consumer<T> | T | / | 純消費,只有輸入沒有輸出 |
Supplier<T> | / | T | 純供應,沒有輸入只有輸出 |
Predicate<T> | T | boolean | 斷言,輸出布林型別 |
UnaryOperator<T> | T | T | 一元函式,輸入輸出型別相同 |
BinaryOperator<T> | (T,T) | T | 二元函式,輸入輸出型別相同 |
Function<T,R>
使用說明:當鋪,用東西換錢。
先看原始碼
@FunctionalInterface public interface Function<T, R> { // 入參型別為T,處理後,返回型別為R的結果。 R apply(T t); // 入參為 Function 物件 before,返回一個 Function 物件 result。 // 執行時,先執行一次 before.apply 方法的,再執行 result.apply default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } // 入參為 Function 物件 after,返回一個 Function 物件 result。 // 執行時,先執行一次 result.apply 方法的,再執行 after.apply default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } // 靜態方法,返回一個只會返回入參的 Function 物件。 // 相當於 t->t 的簡寫,主要用於返回 lambda 表示式 static <T> Function<T, T> identity() { return t -> t; } }
實踐
apply
方法,處理傳入的引數並返回。
public void doubleNum() { Function<Integer, String> d = integer -> String.valueOf(2 * integer); System.out.println(d.apply(10)); }
compose
方法,是傳入一個前置處理的函式,前置函式的返回結果是 apply
的入參。
public void compose() { Function<Integer, String> d = integer -> { System.out.println("real double apply begins, param is " + integer); return String.valueOf(2 * integer); }; //先將輸入的字串轉為Integer,再乘法。compose的入參可以指定,輸出一定要是apply的入參 Function<String, String> compose = d.compose(s -> { System.out.println("compose begins, str to integer, str is " + s); return Integer.valueOf(s); }); System.out.println("final result is " + compose.apply("10")); }
andThen
方法,傳入一個後置函式,可以形成鏈式處理。
public void andThen() { Function<Integer, String> d = integer -> { System.out.println("real double apply begins, param is " + integer); return String.valueOf(2 * integer); }; //andThen傳入的是apply的結果,處理後的型別可以指定 Function<Integer, String> andThen = d.andThen(s -> { System.out.println("and then begins"); return "after and then, result is " + s; }); System.out.println(andThen.apply(10)); }
identity
,是 t -> t
的一種替代式寫法。
public void identify() { List<Student> list = new ArrayList<>(); list.add(new Student("1", "張三")); list.add(new Student("2", "李四")); //兩種寫法等效的 Map<String, Student> map1 = list.stream().collect(Collectors.toMap(Student::getId, s -> s)); Map<String, Student> map2 = list.stream().collect(Collectors.toMap(Student::getId, Function.identity())); }
其他型別 Function
介面 | 函式 | 說明 |
---|---|---|
BiFunction | R apply(T t, U u) | 二元入參 |
DoubleFunction | R apply(double value) | 輸入為double |
DoubleToIntFunction | int applyAsInt(double value) | 輸入double,輸出int |
DoubleToLongFunction | long applyAsLong(double value) | 輸入double,輸入long |
IntFunction | R apply(int value) | 輸入為int |
IntToDoubleFunction | double applyAsDouble(int value) | 輸入int,輸出double |
IntToLongFunction | long applyAsLong(int value) | 輸入int,輸出long |
LongFunction | R apply(long value) | 輸入為long |
LongToDoubleFunction | double applyAsDouble(long value) | 輸入long,輸出double |
LongToIntFunction | int applyAsInt(long value) | 輸入long,輸出int |
ToLongFunction | long applyAsLong(T value) | 輸出為long |
ToLongBiFunction | long applyAsLong(T t, U u) | 二元入參,輸出為long |
ToIntFunction | int applyAsInt(T value) | 輸出為int |
ToIntBiFunction | int applyAsInt(T t, U u) | 二元入參,輸出為int |
ToDoubleFunction | double applyAsDouble(T value) | 輸出為long |
ToDoubleBiFunction | double applyAsDouble(T t, U u) | 二元入參,輸出為long |
Consumer<T>
使用說明:貔貅,光進不出。
先看原始碼
@FunctionalInterface public interface Consumer<T> { //處理邏輯 void accept(T t); //傳入一個後置函式 default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
實踐
Consumer
主要是用於提供一個可以對入參處理的函式,比如用在 foreach
。
public static void main(String[] args) { Consumer<String> c1 = str -> System.out.println("this is the first time, word is " + str); Consumer<String> c2 = str -> System.out.println("this is the second time, word is " + str); List<String> stringList = Arrays.asList("I", "am", "the", "demo"); stringList.forEach(c1); stringList.forEach(c1.andThen(c2)); }
其他型別 Consumer
介面 | 函式 | 說明 |
---|---|---|
BiConsumer | accept(T t, U u) | 二元入參 |
DoubleConsumer | void accept(double value) | 入參為double |
IntConsumer | void accept(int value) | 入參為int |
LongConsumer | void accept(long value) | 入參為long |
ObjDoubleConsumer | void accept(T t, double value) | 後參為double |
ObjIntConsumer | void accept(T t, int value) | 後參為int |
ObjLongConsumer | void accept(T t, long value) | 後參為long |
Supplier<T>
使用說明:老好人,散財童子,啥都不要。
先看原始碼
Supplier
的原始碼就比較簡單了,只有一個 get
方法,返回指定型別物件。
@FunctionalInterface public interface Supplier<T> { T get(); }
實踐
public void demo() { Supplier<Double> random = Math::random; System.out.println(random.get()); Supplier<String> s = String::new; String result = Stream.of("this", "is", "is", "a", "demo").findFirst().orElseGet(s); System.out.println(result); }
其他型別 Supplier
介面 | 函式 | 說明 |
---|---|---|
BooleanSupplier | boolean getAsBoolean() | 返回boolean |
DoubleSupplier | double getAsDouble() | 返回double |
IntSupplier | int getAsInt() | 返回int |
LongSupplier | long getAsLong() | 返回long |
Predicate<T>
使用說明:預言家,判斷你是不是狼人。
先看原始碼
@FunctionalInterface public interface Predicate<T> { //業務判斷邏輯 boolean test(T t); //and 邏輯 default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } //取非邏輯 default Predicate<T> negate() { return (t) -> !test(t); } //or 邏輯 default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); } //靜態方法,返回一個Predicate函式,用於判斷兩個物件是否相等 static <T> Predicate<T> isEqual(Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); } }
實踐
Predicate
主要用於有布林型別的場景。
public static void demo() { Predicate<Integer> p1 = integer -> integer > 10; Predicate<Integer> p2 = integer -> integer < 5; System.out.println("7 > 10 :" + p1.test(7)); System.out.println("7 !< 5 :" + p2.negate().test(7)); System.out.println("7 > 10 && 7 < 5 : " + p1.and(p2).test(7)); System.out.println("0 > 10 || 0 < 5 : " + p1.or(p2).test(0)); System.out.println("null equals null : " + Predicate.isEqual(null).test(null)); }
其他型別 Predicate
介面 | 函式 | 說明 |
---|---|---|
BiPredicate | boolean test(T t, U u) | 二元入參 |
DoublePredicate | boolean test(double value) | 入參double |
IntPredicate | boolean test(int value) | 入參返回int |
LongPredicate | boolean test(long value) | 入參返回long |
UnaryOperator<T>
使用說明:匯率結算,用錢換錢。
先看原始碼
UnaryOperator
繼承 Function
,只不過是輸入輸出型別相同。
public interface UnaryOperator<T> extends Function<T, T> { static <T> UnaryOperator<T> identity() { return t -> t; } }
實踐
public void unaryDemo() { Function<Integer, Integer> squareFunc = integer -> integer * integer; UnaryOperator<Integer> squareUnary = integer -> integer * integer; System.out.println(squareFunc.apply(9)); System.out.println(squareUnary.apply(9)); }
BinaryOperator<T>
使用說明:合成機器,兩個寶石合成一個寶石。
先看原始碼
BinaryOperator
繼承自 BiFunction
,只不過是兩個入參以及返回型別相同。
同時, BinaryOperator
提供了 minBy
和 maxBy
兩個靜態方法。
@FunctionalInterface public interface BinaryOperator<T> extends BiFunction<T,T,T> { //根據比較器,返回較小的那個 public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) { Objects.requireNonNull(comparator); return (a, b) -> comparator.compare(a, b) <= 0 ? a : b; } //根據比較器,返回較大的那個 public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) { Objects.requireNonNull(comparator); return (a, b) -> comparator.compare(a, b) >= 0 ? a : b; } }
實踐
比如整數四則運算,兩個輸入和輸出是一樣的型別。
public void multiply() { BinaryOperator<Integer> multiply = (i1, i2) -> i1 * i2; System.out.println(multiply.apply(3, 5)); }
minBy
和 maxBy
,按比較器輸出。
public void compare() { BinaryOperator<Integer> min = BinaryOperator.minBy(Comparator.comparingInt(i -> i)); BinaryOperator<Integer> max = BinaryOperator.maxBy(Comparator.comparingInt(i -> i)); System.out.println(min.apply(1,2)); System.out.println(max.apply(1,2)); }
總結
函式式介面,歸根究底還是介面,定義的是標準行為。但是函式式介面又只有一個抽象函式,使得我們可以不關注函式名字到底是什麼,而專注於業務邏輯。
JDK 提供的這一套函式式介面,全部在 java.util.function
包中,基本上可以滿足我們常用場景的使用需求。這些介面有些抽象,但只要將關注點轉移到這個介面到底能幹什麼,就不再難理解了。當然,如果還有特殊的使用場景,可以通過自定義函式式介面解決。
注:本文配套程式碼可在 github
檢視: stream-and-lambda
- stream and lambda(18) - 終止操作之 stream 收集器 collectors
- stream and lambda(15) - 終止操作之 stream 陣列操作 toArray
- stream and lambda(13) - 終止操作之計數 count 與比較 min、max
- stream and lambda(12) - 終止操作之查詢與匹配(findAny、findFirst、allMatch、anyMatch、noneMatch)
- stream and lambda(11) - Optional 簡介
- stream and lambda(10) - 中間操作之排序sorted與除錯peek
- stream and lambda(9) - 中間操作之map操作(map、flatmap)
- stream and lambda(8) - 中間操作之篩選操作(filter、distinct、limit、skip)
- stream and lambda(7) - stream 的建立
- stream and lambda(6) - stream 簡介
- stream and lambda(5) - lambda 表示式最佳實踐
- stream and lambda(3) - jdk 提供的函式式介面
- stream and lambda(2) - 函式式介面簡介
- stream and lambda(1) - lambda 簡介