【Java】函数式编程
1 函數式接口
1.1 概念
函數式接口是有且僅有一個抽象方法的接口,可以包括靜態和默認方法。
@FunctionalInterface:加上注解,檢測是否的函數式接口
@FunctionalInterface public interface MyFunctionInterface {public abstract void method();static void method2() {}default void method3() {} }1.2 函數式接口的使用
一般可以作為方法的參數和返回值類型
public static void show(MyFunctionInterface myInter){myInter.method();}public static void main(String[] args) {show(new MyFunctionInterface() {@Overridepublic void method() {System.out.println("使用匿名內部類重寫接口中的抽象方法");}});show(()->{System.out.println("使用lambda表達式重寫接口的抽象方法");});show(()-> System.out.println("使用簡化lambda表達式重寫接口的抽象方法"));}2 函數式編程
2.1 性能浪費案例
public static void showLog(int level, String message){if(level==1){System.out.println(message);}}public static void main(String[] args) {String msg1 = "Hello";String msg2 = "Hello";String msg3 = "Hello";showLog(1,msg1+msg2+msg3);}調用showLog方法,第二個參數式拼接后的字符串,如果等級不是1,message不需要輸出,存在浪費。
2.2 Lambda優化案例
@FunctionalInterface public interface MessageBuider {String buiderMessage(); } public static void showLog(int level, MessageBuider mb){if(level==1){System.out.println(mb.buiderMessage());;}}public static void main(String[] args) {String m1 = "Hello";String m2 = "Zhangsan";showLog(1, ()->{return m1+m2;});}這里只有滿足條件才會調用接口的方法進行字符串拼接。如果不滿足就不會進行字符串拼接,沒有了性能的浪費。
2.3 使用Lambda作為參數和返回值
作為參數
//Runnable是一個函數式接口public static void startThread(Runnable run){new Thread(run).start();}public static void main(String[] args) {startThread(()->System.out.println("開啟線程"+Thread.currentThread().getName()));}作為返回值
//Comparator是一個函數式接口public static Comparator<String> getComparator(){return (o1, o2) -> o2.length()-o1.length();}public static void main(String[] args) {String[] arr = {"aaa","bbbbb","cccccc"};Arrays.sort(arr);System.out.println(Arrays.toString(arr));//[aaa, bbbbb, cccccc]Arrays.sort(arr,getComparator());System.out.println(Arrays.toString(arr));//[cccccc, bbbbb, aaa]}3 常用的函數式接口
3.1 Supplier接口
僅包含一個無參方法:T get()
用來獲取一個泛型參數指定類型的對象數據。由于這是一個函數式接口,這也就意味著對應的Lambda表達式需要“對外提供”一個符合泛型類型的對象數據。
練習:應用Supplier求數組最大值
public static int getMax(Supplier<Integer> sup){return sup.get();}public static void main(String[] args) {int[] arr = {5,6,2,4,1,7,3};int ans = getMax(() -> {int max = arr[0];for (int i : arr) {if (i > max) {max = i;}}return max;});System.out.println(Arrays.toString(arr));//[5, 6, 2, 4, 1, 7, 3]System.out.println(ans);//7}3.2 Consumer接口
consumer接口是一個消費型接口,泛型執行什么類型,可以使用accept方法消費什么類型的數據,至于怎么消費,需要自定義。
public static void method(String name, Consumer<String> con){con.accept(name);}public static void main(String[] args) {//消費方式:輸出method("張三", (name)->{System.out.println(name);});//可以替換為method("張三", System.out::println);//消費方式:反轉輸出method("張三",(name)->{System.out.println(new StringBuilder(name).reverse().toString());});}默認方法andThen:如果一個方法的參數和返回值全都是Consumer 類型,那么就可以實現效果:消費數據的時候,首先做一個操作,然后再做一個操作,實現組合。而這個方法就是Consumer 接口中的default方法andThen
default Consumer<T> andThen(Consumer<? super T> after) {Objects.requireNonNull(after);return (T t) ‐> { accept(t); after.accept(t); }; }備注: java.util.Objects 的requireNonNull 靜態方法將會在參數為null時主動拋出
NullPointerException 異常。這省去了重復編寫if語句和拋出空指針異常的麻煩。
練習andThen:格式化打印信息
下面的字符串數組當中存有多條信息,請按照格式“ 姓名:XX。性別:XX。”的格式將信息打印出來。要求將打印姓名的動作作為第一個Consumer 接口的Lambda實例,將打印性別的動作作為第二個Consumer 接口的Lambda實例,將兩個Consumer 接口按照順序“拼接”到一起。
3.3 Predicate接口
作用:對數據類型的數據進行判斷,結果返回boolean值
抽象方法test:用來對指定數據類型的數據進行判斷的方法
public static boolean checkString(String s, Predicate<String> pre){return pre.test(s);}public static void main(String[] args) {String s = "abcd";boolean check = checkString(s, (str) -> str.length() > 5);System.out.println(check);}默認方法and:
既然是條件判斷,就會存在與、或、非三種常見的邏輯關系。其中將兩個Predicate 條件使用“與”邏輯連接起來實現“并且”的效果時,可以使用default方法and 。其JDK源碼為:
定義兩個判斷條件:字符串長度大于5,字符串包含a,條件要同時滿足
public static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2){return pre1.and(pre2).test(s);//相當于👇//return pre1.test(s) && pre2.test(s);}public static void main(String[] args) {String s = "abcdef";boolean check = checkString(s, (str) -> str.length() > 5,(str) -> str.contains("a"));System.out.println(check);}默認方法or
與and 的“與”類似,默認方法or 實現邏輯關系中的“或”。JDK源碼為:
定義兩個判斷條件:字符串長度大于5,字符串包含a,條件滿足一個即可
public static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2){return pre1.or(pre2).test(s);//相當于👇 // return pre1.test(s) || pre2.test(s);}public static void main(String[] args) {String s = "a";boolean check = checkString(s, (str) -> str.length() > 5,(str) -> str.contains("a"));System.out.println(check);}默認方法negate:“非”(取反)默認方法negate 的JDK源代碼為:
default Predicate<T> negate() {return (t) ‐> !test(t); } public static boolean checkString(String s, Predicate<String> pre){return pre.negate().test(s);//相當于👇//return !pre.test(s);}public static void main(String[] args) {String s = "a";boolean check = checkString(s, (str) -> str.length() > 5);System.out.println(check);}練習:信息集合篩選
數組當中有多條“姓名+性別”的信息如下,請通過Predicate 接口的拼裝將符合要求的字符串篩選到集合ArrayList 中,需要同時滿足兩個條件:
3.4 Function接口
java.util.function.Function<T,R> 接口用來根據一個類型的數據得到另一個類型的數據,前者稱為前置條件,后者稱為后置條件。
使用場景:根據類型T的參數獲取類型R的結果
抽象方法:apply
public static void change(String s, Function<String, Integer> fun){Integer in = fun.apply(s);System.out.println(in);}public static void main(String[] args) {String s = "1234";change(s, (str)->{return Integer.parseInt(s);});}默認方法:andThen
把String類型的123轉換為Integer類型,加上10后,再轉為String類型輸出
練習:自定義函數模型的拼接
請使用Function 進行函數模型的拼接,按照順序需要執行的多個函數操作為:
總結
以上是生活随笔為你收集整理的【Java】函数式编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 推荐几个Android开发非常有用的工具
- 下一篇: 如何下载Android源码(非常详细,含