Java 8 - 03 Lambda 函数式接口Predicate Consumer Function Supplier
文章目錄
- Pre
- Predicate 斷言型函數式接口
- Consumer 消費型函數式接口
- Function 功能型函數式接口
- Supplier 供給型函數式接口
- 小結
- 函數式接口如何處理異常信息
Pre
Java 8 - 02 Lambda Expression中我們討論了函數式接口, 函數式接口定義且只定義了一個抽象方法。因為抽象方法的簽名可以描述Lambda表達式的簽名。函數式接口的抽象方法的簽名稱為函數描述符。
所以為了應用不同的Lambda表達式,我們需要一套能夠描述常見函數描述符的函數式接口Java API中已經有了幾個函數式接口,比如 Comparable 、 Runnable 和Callable 。
Java 8 在 java.util.function 包中引入了幾個新的函數式接口,比比較常用的Predicate 、 Consumer 和 Function 等 。
Predicate 斷言型函數式接口
package java.util.function;import java.util.Objects;/*** Represents a predicate (boolean-valued function) of one argument.** <p>This is a <a href="package-summary.html">functional interface</a>* whose functional method is {@link #test(Object)}.** @param <T> the type of the input to the predicate** @since 1.8*/ @FunctionalInterface public interface Predicate<T> {boolean test(T t);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);}default Predicate<T> or(Predicate<? super T> other) {Objects.requireNonNull(other);return (t) -> test(t) || other.test(t);}static <T> Predicate<T> isEqual(Object targetRef) {return (null == targetRef)? Objects::isNull: object -> targetRef.equals(object);} }標注了@FunctionalInterface , 抽象方法只能包含一個 , default 方法 和 static的方法除外 , 比如 Predicate ,只有test是抽象方法,其他的幾個都是default級別和static修飾的方法,所以Predicate也是一個函數式接口 。
java.util.function.Predicate<T> 接口定義了一個名叫 test 的抽象方法,它接受泛型T 對象,并返回一個 boolean 。
如果需要表示一個涉及類型 T 的布爾表達式時,就可以使用這個接口
舉個例子
import com.artisan.domain.Enginner;import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Predicate;/*** @author 小工匠* @version v1.0* @create 2020-05-16 9:13* @motto show me the code ,change the word* @blog https://artisan.blog.csdn.net/* @description**/public class PredicateDemo {/*** 過濾符合規則的泛型類* @param list* @param predicate* @param <T>* @return*/private static <T> List<T> filter(List<T> list , Predicate<T> predicate){List<T> targetList = new ArrayList<>();for (T t :list){if (predicate.test(t)){targetList.add(t);}}return targetList;}public static void main(String[] args) {List<Enginner> enginnerList = Arrays.asList(new Enginner("Java", 18), new Enginner("GO", 20));List<Enginner> goEngineerList = filter(enginnerList, enginner -> enginner.getJob().equals("GO"));System.out.println(goEngineerList);} }我們通過 filter(enginnerList, (enginner) -> enginner.getJob().equals("GO")); 第二個參數 (enginner) -> enginner.getJob().equals("GO") 方法簽名返回的是Boolean,所以可以使用Predicate這個JDK8中提供的接口 。
當然了你也可以直接寫一個類似Predicate的函數式接口來供自己調用 ,如下
@FunctionalInterface public interface Filter<T> {boolean filter(T t);}那么就變成了如下的樣子
private static <T> List<T> filterCustom(List<T> list , Filter<T> filter){List<T> targetList = new ArrayList<>();for (T t :list){if (filter.filter(t)){targetList.add(t);}}return targetList;}main方法中調用如下
List<Enginner> javaEngineerList= filterCustom(enginnerList, enginner -> enginner.getJob().equals("Java"));System.out.println(javaEngineerList);Consumer 消費型函數式接口
package java.util.function;import java.util.Objects;/*** Represents an operation that accepts a single input argument and returns no* result. Unlike most other functional interfaces, {@code Consumer} is expected* to operate via side-effects.** @since 1.8*/ @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); };} }標注了@FunctionalInterface , 抽象方法只能包含一個 , default 方法 和 static的方法除外 , 比如 Consumer,只有accept是抽象方法Consumer是default級的方法,所以Consumer也是一個函數式接口 。
java.util.function.Consumer<T> 定義了一個名叫 accept 的抽象方法,它接受泛型 T的對象,沒有返回( void )。
如果需要訪問類型 T 的對象,并對其執行某些操作,就可以使用這個接口
舉個例子
比如,你可以用它來創建一個 forEach 方法,接受一個 Integers 的列表,并對其中每個元素執行操作。 假設我們要使用這個 forEach 方法,并配合Lambda來打印列表中的所有元素。
import java.util.Arrays; import java.util.List; import java.util.function.Consumer;/*** @author 小工匠* @version v1.0* @create 2020-05-16 20:20* @motto show me the code ,change the word* @blog https://artisan.blog.csdn.net/* @description**/public class ComusmerDemo {public static <T> void doForEach(List<T> tList, Consumer<T> consumer){for (T t: tList ) {consumer.accept(t);}}public static void main(String[] args) {// 第二個參數 Lambda是 Consumer 中accept 方法的實現doForEach(Arrays.asList(1,2,3,5,7),(Integer i) -> System.out.println(i));} }Function 功能型函數式接口
package java.util.function;import java.util.Objects;/*** Represents a function that accepts one argument and produces a result.** @param <T> the type of the input to the function* @param <R> the type of the result of the function** @since 1.8*/ @FunctionalInterface public interface Function<T, R> {R apply(T t);default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {Objects.requireNonNull(before);return (V v) -> apply(before.apply(v));}default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {Objects.requireNonNull(after);return (T t) -> after.apply(apply(t));}static <T> Function<T, T> identity() {return t -> t;} }java.util.function.Function<T, R> 接口定義了一個叫作 apply 的方法,它接受一個泛型 T 的對象,并返回一個泛型 R 的對象。
如果我們需要定義一個Lambda,將輸入對象的信息映射到輸出,就可以使用這個接口 ,舉個例子提取工程師的職位或把字符串映射為它的長度等等
來個小demo : 利用Function 來創建一個 map 方法,以將一個 String 列表映射到包含每個
String 長度的 Integer 列表
Supplier 供給型函數式接口
package java.util.function;/*** Represents a supplier of results.** <p>There is no requirement that a new or distinct result be returned each* time the supplier is invoked.* * @param <T> the type of results supplied by this supplier** @since 1.8*/ @FunctionalInterface public interface Supplier<T> {/*** Gets a result.** @return a result*/T get(); }無參數,返回一個結果。
import com.artisan.domain.Enginner;import java.util.function.Supplier;/*** @author 小工匠* @version v1.0* @create 2020-05-16 21:08* @motto show me the code ,change the word* @blog https://artisan.blog.csdn.net/* @description**/public class SupplierDemo {public static <T> T doGet(Supplier<T> supplier) {return supplier.get();}public static void main(String[] args) {Enginner enginneer = new Enginner("JAVA", 18);String s = doGet(() -> enginneer.getJob());System.out.println(s);} }小結
我們介紹了4個泛型函數式接口: Predicate<T> 、 Consumer<T> 、Function<T,R> 、Supplier<T>
還有些函數式接口專為某些類型而設計。
回顧一下:Java類型要么是引用類型(比如 Byte 、 Integer 、 Object 、 List ),要么是原始類型(比如 int 、 double 、 byte 、 char ).
但是泛型(比如 Consumer<T> 中的 T )只能綁定到引用類型。這是由泛型內部的實現方式造成的。因此,在Java里有一個將原始類型轉換為對應的引用類型的機制。這個機制叫作裝箱(boxing)。相反的操作,也就是將引用類型轉換為對應的原始類型,叫作拆箱(unboxing).
Java還有一個自動裝箱機制來幫助程序員執行這一任務:裝箱和拆箱操作是自動完成的。
比如,這就是為什么下面的代碼是有效的(一個 int 被裝箱成為Integer )
List<Integer> list = new ArrayList<>(); for (int i = 0; i < 100; i++){list.add(i); }但這在性能方面是要付出代價的。裝箱后的值本質上就是把原始類型包裹起來,并保存在堆里。因此,裝箱后的值需要更多的內存,并需要額外的內存搜索來獲取被包裹的原始值。
Java 8為我們前面所說的函數式接口帶來了一個專門的版本,以便在輸入和輸出都是原始類型時避免自動裝箱的操作。
比如,在下面的代碼中,使用 IntPredicate 就避免了對值 1000 進行裝箱操作,但要是用 Predicate<Integer> 就會把參數 1000 裝箱到一個 Integer 對象中:
public class IntPredicateDemo {public static void main(String[] args) {// 無裝箱IntPredicate intPredicate = (int i) -> i % 2 == 0;intPredicate.test(1000);// 裝箱Predicate<Integer> predicate = (Integer i) -> i % 2 == 0;predicate.test(1000);} }一般來說,針對專門的輸入參數類型的函數式接口的名稱都要加上對應的原始類型前綴,比如 DoublePredicate 、 IntConsumer 、 LongBinaryOperator 、 IntFunction 等。
Function接口還有針對輸出參數類型的變種: ToIntFunction<T> 、 IntToDoubleFunction 等
來個小測驗
請構造一個可以利用這些函數式接口的有效Lambda 表達式: (1) T->R (2) (int, int)->int (3) T->void (4) ()->T (5) (T, U)->R (1) Function<T,R> 不錯。它一般用于將類型 T 的對象轉換為類型 R 的對象(比如 Function<Apple, Integer> 用來提取蘋果的重量)。 (2) IntBinaryOperator 具有唯一一個抽象方法,叫作 applyAsInt ,它代表的函數描述 符是 (int, int) -> int 。 (3) Consumer<T> 具有唯一一個抽象方法叫作 accept ,代表的函數描述符是 T -> void 。 (4) Supplier<T> 具有唯一一個抽象方法叫作 get ,代表的函數描述符是 ()-> T 。或者, Callable<T> 具有唯一一個抽象方法叫作 call ,代表的函數描述符是 () -> T 。 (5) BiFunction<T, U, R> 具有唯一一個抽象方法叫作 apply ,代表的函數描述符是 (T, U) -> R 。最后 總結關于函數式接口和Lambda
函數式接口如何處理異常信息
Note : ,任何函數式接口都不允許拋出受檢異常(checked exception)。如果你需要Lambda表達式來拋出異常,有兩種辦法:定義一個自己的函數式接口,并聲明受檢異常,或者把Lambda包在一個 try/catch 塊中。
自己的函數式接口如下:
@FunctionalInterface public interface BufferedReaderProcessor {String process(BufferedReader b) throws IOException; }調用
BufferedReaderProcessor p = (BufferedReader br) -> br.readLine();但是你可能是在使用一個接受函數式接口的API,比如 Function<T, R> ,沒有辦法自己創建一個。這種情況下, 可以顯式捕捉受檢異常:
Function<BufferedReader, String> f = (BufferedReader b) -> { try {return b.readLine(); } catch(IOException e) {throw new RuntimeException(e);} }; 《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的Java 8 - 03 Lambda 函数式接口Predicate Consumer Function Supplier的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java 8 - 02 Lambda E
- 下一篇: Java 8 - 04 类型检查、类型推