Lambda-函数式接口(1)
生活随笔
收集整理的這篇文章主要介紹了
Lambda-函数式接口(1)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、函數式接口
#1.概念
函數式接口在Java中是指:有且僅有一個抽象方法的接口。 函數式接口,即適用于函數式編程場景的接口。而Java中的函數式編程體現就是Lambda,所以函數式接口就是可以適用于Lambda使用的接口。只有確保接口中有且僅有一個抽象方法,Java中的Lambda才能順利地進行推導。 備注:“語法糖”是指使用更加方便,但是原理不變的代碼語法。例如在遍歷集合時使用的for-each語法,其實底層的實現原理仍然是迭代器,這便是“語法糖”。從應用層面來講,Java中的Lambda可以被當做是匿名內部類的“語法糖”,但是二者在原理上是不同的。#2.格式
只要確保接口中有且僅有一個抽象方法即可:
修飾符?interface?接口名稱?{public?abstract?返回值類型?方法名稱(可選參數信息);????//?其他非抽象方法內容 } 由于接口當中抽象方法的 public abstract是可以省略的,所以定義一個函數式接口就很簡單了: public?interface?MyFunctionalInterface?{void myMethod(); }#3.@FunctionalInterface注解
與@Override注解的作用類似,Java8中專門為函數式接口引入了一個新的注解:@FunctionalInterface。
該注解可以用于一個接口的定義上:
@FunctionalInterface public?interface?MyFunctionalInterface?{????void?myMethod(); } 一旦使用該注解來定義接口,編譯器將會強制檢查該接口是否確實有且僅有一個抽象方法,否則將會報錯。需要注 意的是,即使不使用該注解,只要滿足函數式接口的定義,這仍然是一個函數式接口,使用起來都一樣。#4.自定義函數式接口
對于剛剛定義好的@FunctionalInterface函數接口,典型使用場景就是作為方法的參數:
public?class?Demo09FunctionalInterface?{?//?使用自定義的函數式接口作為方法參數????private?static?void?doSomething(MyFunctionalInterface?inter)?{????????inter.myMethod();?//?調用自定義的函數式接口方法????}????????public?static?void?main(String[]?args)?{????????//?調用使用函數式接口的方法????????doSomething(()?‐>?System.out.println("Lambda執行啦!"));????} }二、函數式編程
在兼顧面向對象特性的基礎上,Java語言通過Lambda表達式與方法引用等,為開發者打開了函數式編程的大門。 下面我們做一個初探。#1.Lambda的延遲執行
有些場景的代碼執行后,結果不一定會被使用,從而造成性能浪費。而Lambda表達式是延遲執行的,這正好可以 作為解決方案,提升性能。 性能浪費的日志案例 注:日志可以幫助我們快速的定位問題,記錄程序運行過程中的情況,以便項目的監控和優化。 一種典型的場景就是對參數進行有條件使用,例如對日志消息進行拼接后,在滿足條件的情況下進行打印輸出: public?class?Demo01Logger?{private?static?void?log(int?level,?String?msg)?{if?(level?==?1)?{????????????System.out.println(msg);????????}????}?????public?static?void?main(String[]?args)?{????????String?msgA?=?"Hello";????????String?msgB?=?"World";????????String?msgC?=?"Java";?????????log(1,?msgA?+?msgB?+?msgC);????} } 這段代碼存在問題:無論級別是否滿足要求,作為log方法的第二個參數,三個字符串一定會首先被拼接并傳入方法內,然后才會進行級別判斷。如果級別不符合要求,那么字符串的拼接操作就白做了,存在性能浪費。 備注:SLF4J是應用非常廣泛的日志框架,它在記錄日志時為了解決這種性能浪費的問題,并不推薦首先進行字符串的拼接,而是將字符串的若干部分作為可變參數傳入方法中,僅在日志級別滿足要求的情況下才會進行字符串拼接。 例如:LOGGER.debug("變量{}的取值為{}。",?"os",?"macOS"),其中的大括號{}為占位符。如果滿足日志級別要求,則會將“os”和“macOS”兩個字符串依次拼接到大括號的位置;否則不會進行字符串拼接。這也是一種可行解決方案,但Lambda可以做到更好。#2.體驗Lambda的更優寫法
使用Lambda必然需要一個函數式接口:
@FunctionalInterface public?interface?MessageBuilder?{?String?buildMessage(); } 然后對log方法進行改造: 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?); } 這樣一來,只有當級別滿足要求的時候,才會進行三個字符串的拼接;否則三個字符串將不會進行拼接。 證明Lambda的延遲 下面的代碼可以通過結果進行驗證: public?class?Demo03LoggerDelay?{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(2,?()?‐>?{????????????System.out.println("Lambda執行!");return?msgA?+?msgB?+?msgC;????????});} } 從結果中可以看出,在不符合級別要求的情況下,Lambda將不會執行。從而達到節省性能的效果。 擴展:實際上使用內部類也可以達到同樣的效果,只是將代碼操作延遲到了另外一個對象當中通過調用方法 來完成。而是否調用其所在方法是在條件判斷之后才執行的。#3.使用Lambda作為參數和返回值
如果拋開實現原理不說,Java中的Lambda表達式可以被當作是匿名內部類的替代品。如果方法的參數是一個函數式接口類型,那么就可以使用Lambda表達式進行替代。使用Lambda表達式作為方法參數,其實就是使用函數式接口作為方法參數。 例如 java.lang.Runnable 接口就是一個函數式接口,假設有一個 startThread 方法使用該接口作為參數,那么就可以使用Lambda進行傳參。這種情況其實和 Thread 類的構造方法參數為 Runnable 沒有本質區別。 public class Demo04Runnable {private static void startThread(Runnable task) {new Thread(task).start(); }public static void main(String[] args) {startThread(() ‐> System.out.println("線程任務執行!"));} } 類似地,如果一個方法的返回值類型是一個函數式接口,那么就可以直接返回一個Lambda表達式。當需要通過一 個方法來獲取一個 java.util.Comparator 接口類型的對象作為排序器時,就可以調該方法獲取。 import java.util.Arrays; import java.util.Comparator; public class Demo06Comparator {private static Comparator<String> newComparator() {return (a, b) ‐> b.length() ‐ a.length(); }public static void main(String[] args) {String[] array = { "abc", "ab", "abcd" };System.out.println(Arrays.toString(array));Arrays.sort(array, newComparator());System.out.println(Arrays.toString(array));} } 其中直接return一個Lambda表達式即可。總結
以上是生活随笔為你收集整理的Lambda-函数式接口(1)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: StringBuilder的原理
- 下一篇: Lambda-常用函数式接口