Java函数式接口看这一篇就够了
目錄:
1.函數式接口的基本概念和格式
2.函數式編程
3.函數式接口作為方法的參數和方法的返回值
4.常用函數式接口
1.函數式接口的基本概念和格式
1.函數式接口的基本概念:
函數式接口在Java中是指:有且僅有一個抽象方法的接口。
函數式接口,即適用于函數式編程場景的接口。而Java中的函數式編程體現就是Lambda,所以函數式接口就是可 以適用于Lambda使用的接口。只有確保接口中有且僅有一個抽象方法,Java中的Lambda才能順利地進行推導。
如果對Lambda表達式不是很清除那么可以參照這一篇博客:Lambda表達式詳解
介紹一個語法糖概念:
語法糖:是指使用更加方便,但是原理不變的代碼語法。例如在遍歷集合時使用的for-each語法,其實 底層的實現原理仍然是迭代器,這便是“語法糖”。從應用層面來講,Java中的Lambda可以被當做是匿名內部 類的“語法糖”,但是二者在原理上是不同的。
2.格式:
只要滿足有一個抽象方法的接口即可,當然也可以包含其他的方法(默認靜態私有等等)
修飾符 interface 接口名稱{ (public abstract) 返回值類型 方法名稱(可選參數信息); // 其他非抽象方法內容 }由于接口中默認類型為public abstract 所以帶不帶都行
3.@FunctionalInterface注解:
Java 8中專門為函數式接口引入了一個新的注解: @FunctionalInterface。該注 解可用于一個接口的定義上
@FunctionalInterfacepublic interface MyFunctionalInterface{ void myMethod(); }我們這一個注解當我們在定義函數式接口的時候發送了錯誤,那么注解的編譯就不通過:
當我們去掉一個抽象的方法的時候就,注意就會正常通過編譯:
一旦使用該注解來定義接口,編譯器將會強制檢查該接口是否確實有且僅有一個抽象方法,否則將會報錯。需要注 意的是,即使不使用該注解,只要滿足函數式接口的定義,這仍然是一個函數式接口,使用起來都一樣。
2.函數式編程
1.Lambda表達式的延遲執行
看一段代碼:
分析一下這段代碼,發現存在浪費,首先log方法傳進兩個參數,第一個參數是一個數字,第二個參數是一個拼接后的字符串,那么正常調用的時候我們就要先把字符串拼接好后再調用log函數,如果第一個參數不是1,那么拼接字符串就顯得沒用,白拼接了,這樣就造成了浪費,這時候就可以使用Lambda解決
想要解決問題,那么首先我們了解Lambda的使用特點和前提:
1.使用特點:延遲加載
2.使用前提:必須存在函數式接口
具體解決的代碼實現:
package untl1; public class Demo02LoggerLambda {private static void log(int level, MessageBuilder builder){if (level == 1){System.out.println(builder.buildMessage());}}public static void main(String[] args){String msgA = "Hello";String msgB = "World";String msgC = "Java";log(1, ()-> msgA+msgB+msgC);} } @FunctionalInterface interface MessageBuilder {String buildMessage(); }此段代碼就完美的解決上述問題,那么,我們如何證實在第一個參數不是1的時候不進行字符串的拼接呢,我們可以在第二個參數加一個書輸出語句即
package untl1; public class Demo02LoggerLambda {private static void log(int level, MessageBuilder builder){if (level == 1){System.out.println(builder.buildMessage());}}public static void main(String[] args){String msgA = "Hello";String msgB = "World";String msgC = "Java";log(1, ()-> {System.out.println("我第一個參數是1");return msgA+msgB+msgC+1;});log(2, ()-> {System.out.println("我第一個參數不是1");return msgA+msgB+msgC+2;});} } @FunctionalInterface interface MessageBuilder {String buildMessage(); } 運行結果: 我第一個參數是1 HelloWorldJava13.函數式接口作為方法的參數和方法的返回值
作為方法的參數,上邊修正過的例子就是,作為方法的返回值如下例:
package untl1; public class Demo02LoggerLambda {private static MessageBuilder log(){return (a,b)->{return a+b;};}public static void main(String[] args){MessageBuilder a=log();System.out.println(a.buildMessage("hello","world"));} } @FunctionalInterface interface MessageBuilder {String buildMessage(String a,String b); } 運行結果: helloworld注意return中的a和b是兩個變量名稱,可以隨意更改
4.常用函數式接口
1.supplier接口:
(1) 簡介:
java.util.function.Supplier<T> 接口僅包含一個無參的方法: T get() 。用來獲取一個泛型參數指定類型的對 象數據。由于這是一個函數式接口,這也就意味著對應的Lambda表達式需要“對外提供”一個符合泛型類型的對象 數據,supplier接口又被稱為生產型接口,指定接口是什么類型,那么接口的get方法就會產生一個什么類型的數據類型
(2)例子:
package untl1; import java.util.function.Supplier; public class MySupplier {public static String getString(Supplier<String> sup){return sup.get();}public static void main(String[] args) {String str=getString(()->"helloworld");System.out.println(str);}} 運行結果: helloworld2.Consumer接口:
(1)簡介:
java.util.function.Consumer<T> 接口則正好與Supplier接口相反,它不是生產一個數據,而是消費一個數據, 其數據類型由泛型決定。Consumer 接口中包含抽象方法 void accept(T t) ,意為消費一個指定泛型的數據。,至于具體怎么消費如何消費需要自定義(輸出或者計算等等…)
(2)l例子:
package untl1; import java.util.function.Consumer; public class MyConsumer {public static void method(String name, Consumer<String> con){con.accept(name);}public static void main(String[] args) {method("我是帥哥",(String name1)-> System.out.println(name1));} }(3).Consumer默認方法andThen
如果一個方法的參數和返回值全都是 Consumer 類型,那么就可以實現效果:消費數據的時候,首先做一個操作, 然后再做一個操作,實現組合。而這個方法就是 Consumer 接口中的default方法 andThen 。下面是JDK的源代碼:
default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after);return (T t) ‐> {accept(t); after.accept(t); }; }其實andThen方法簡單來說就是把兩個Consumer接口組合在一起,在分別對數據進行消費
下面我們不使用andThen和使用andThen的兩個例子做對比:
例子1(不使用andThen):
我們使用andThen之后:
package untl1; import java.util.function.Consumer; public class MyConsumer {public static void method(String str, Consumer<String> a,Consumer<String> b){a.andThen(b).accept(str);}public static void main(String[] args) {method("hello",(String a)-> System.out.println(a),(String b)-> System.out.println(b.toUpperCase()));}} 運行結果和上邊的一樣上述代碼由于是a連接b那么先執行a消費數據,然后再執行消費數據
3.Predicate接口:
(1)簡介;
有時候我們需要對某種類型的數據進行判斷,從而得到一個boolean值結果。這時可以使用 java.util.function.Predicate<T>接口。Predicate 接口中包含一個抽象方法: boolean test(T t) 。
(2)例子:
package untl1; import java.util.function.Predicate; public class MyPredicate {public static boolean checkString(String str, Predicate<String> pre){return pre.test(str);}public static void main(String[] args) {String str="abc";boolean b=checkString(str,a->a.length()>6);System.out.println(b);} } 運行結果: false(3)默認方法and:
既然是條件判斷,就會存在與、或、非三種常見的邏輯關系。其中將兩個 Predicate 條件使用“與”邏輯連接起來實 現“并且”的效果時,可以使用default方法 and。其JDK源碼為
default Predicate<T> and(Predicate<? super T> other) {Objects.requireNonNull(other);return (t) ‐> test(t) && other.test(t); }例子:
我么判斷一個字符串里邊長度是不是為5,里邊是不是包含a這個字符:
(4)默認方法or:
與 and 的“與”類似,默認方法 or 實現邏輯關系中的“或”。
例子:
package untl1; import java.util.function.Predicate; public class MyPredicate {public static boolean checkString(String str, Predicate<String> a,Predicate<String> b){return a.or(b).test(str);//其實就相當于return a.test(str)||b.test(str)}public static void main(String[] args) {boolean a= checkString("abc",str->str.length()==5,str->str.contains("a") );System.out.println(a);} } 運行結果: true(5)默認方法negate:
“或”已經了解了,剩下的“非”(取反)也會簡單。
例子:
package untl1; import java.util.function.Predicate; public class MyPredicate {public static boolean checkString(String str, Predicate<String> a){return a.negate().test(str);}public static void main(String[] args) {boolean a= checkString("abc",str->str.length()==5);System.out.println(a);} } 運行結果: true5.Function接口:
(1)簡介
java.util.function.Function<T,R> 接口用來根據一個類型的數據得到另一個類型的數據,前者稱為前置條件, 后者稱為后置條件。Function 接口中最主要的抽象方法為:R apply(T t) ,根據類型T的參數獲取類型R的結果。 使用的場景例如:將 String 類型轉換為 Integer 類型。
(2)
例子:
(3)默認方法andThen:
同樣進行組合操作:
例子:
我們把String類型的123轉換成Integer加上10,再轉換成String類型:
總結
以上是生活随笔為你收集整理的Java函数式接口看这一篇就够了的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: What?Java这么神奇的lambda
- 下一篇: Java方法的引用(打造Lambda表达