目錄
前言Lambda類型Lambda的域以及訪問限制內置函數接口方法引用靜態方法引用實例方法引用對象方法引用構造方法引用總結前言
上一篇介紹了lambda表達式的語法和使用場景 和 ,今天老顧繼續介紹
Lambda類型
Lambda表達式可以被當做是一個Object。Lambda表達式的類型,叫做“目標類型(target type)”。Lambda表達式的目標類型是“函數接口(functional interface)”。
有一個接口,如果只有一個顯式聲明的抽象方法,那麼它就是一個函數接口。一般用@FunctionalInterface標註出來(也可以不標)。舉例如下
我們看到最後的Comparator接口,聲明瞭兩個方法,好像不符合函數接口的定義,但Comparator確實是函數接口。這個是因為equals方法是Object的,所有的接口都會聲明Object的public方法(雖然大多是隱式的)。所以,Comparator顯式的
雖然Lambda雖然可以當作是Object類型,但需要顯式轉換才行。
我們可以用一個Lambda表達式為一個函數接口賦值:
Runnable r1 = () -> {System.out.println("Hello Lambda!");};
然後再賦值給一個Object:
Object obj = r1;
但卻不能這樣幹:
Object obj = () -> {System.out.println("Hello Lambda!");};
// ERROR! Object is not a functional interface!
必須顯式的轉型成一個函數接口才可以:
Object o = (Runnable) () -> { System.out.println("Hello Lambda!"); };
一個Lambda表達式只有在轉型成一個函數接口後才能被當做Object使用。所以下面這句也不能編譯:
System.out.println( () -> {} ); //錯誤! 目標類型不明
必須先轉型:
System.out.println( (Runnable)() -> {} ); // 正確
我們可以定義一個無參數,無返回值的接口,類似Runnable
@FunctionalInterface
public interface MyRunnable {
public void run();
}
那下面的寫法,都是正確的
Runnable r1 = () -> {System.out.println("Hello Lambda!");};
MyRunnable r2 = () -> {System.out.println("Hello Lambda!");};
這說明一個Lambda表達式可以有多個目標類型(函數接口),只要函數匹配成功即可。
但需注意一個Lambda表達式必須至少有一個目標類型。
Lambda的域以及訪問限制
域即作用域,Lambda表達式中的參數列表中的參數在該Lambda表達式範圍內(域)有效。在作用Lambda表達式內,可以訪問外部的變量:局部變量、類變量和靜態變量,但操作受限程度不一。
1、訪問局部變量
在Lambda表達式外部的局部變量會被JVM隱式的編譯成final類型,因此只能訪問外而不能修改。
2、訪問靜態變量和成員變量
在Lambda表達式內部,對靜態變量和成員變量可讀可寫。
內置函數式接口
小夥伴們有沒有發現,如果使用Lambda表達式,還是需要我們自己寫一個接口定義的
Consumer< T >con 消費型接口: void accept(T t);
Supplier< T >sup供給型接口: T get();
Function< T , R >fun 函數型接口: R apply (T t);
Predicate< T >: 斷言型接口: boolean test(T t);
1、Consumer 消費型接口
接口中的方法為 void accept(T t),1個參數,無返回值。調用方要傳入值,而不需要返回,形象比喻成消費型
這個就是對傳入num參數值,進行相關的處理(消費)。到底進行處理,具體就在
(num) -> System.out.println("消費了" + num)
上面的代碼中,是一個典型的1個參數,無返回值的消費;如果沒有內置的函數接口,那我們就需要自己定義一個,如:
interface MyConsumer{
\tvoid doFunction(T t);
}
我們發現和內置函數接口,沒有什麼區別,就是接口名和方法名稱不一樣而已,其實本質是一樣的,這就是為什麼Java會提供一些內置的函數,這樣可以減少大量的代碼。
2、Supplier供給型接口
接口中的方法 T get(),無參數,有返回值;不需要對方給參數,而是一直返回給對方
上面代碼就是返回一個新的實體對象。
3、Function函數式接口
接口中的方法 R apply (T t),有參數,有返回值;典型的函數,所以形象比喻成函數型接口
接口實現轉換成大寫的字符。
4、Predicate斷言型接口
接口中方法 boolean test(T t),有參數,返回boolean,是一個條件檢查式方法,比喻成斷言型接口
內置的函數接口,大大的提高開發的效率,減少了開發代碼
方法引用
方法引用是Lambda表達式的一個簡化寫法,其語法結構為:
ObjectRef::methodName
左邊可以是類名或者實例名,中間是方法引用符號“::”,右邊是相應的方法名。方法引用可以分為三類。
使用前提:Lambda體中調用方法的參數列表和返回值類型,要和函數式接口中抽象方法的參數列表和返回值類型保持一致
靜態方法引用
先上一個案例
上面的代碼中,我們發現Converter匿名類重載方法中,調用了ReferenceTest的靜態方法String2Int。再有函數接口的方法參數類型 和 返回值類型,跟String2Int是一樣的,入參為String,返回值為Integer。那我們這個時候可以簡化成
直接用靜態方法進行賦值,是不是很簡潔。
實例方法引用
如果函數式接口的實現恰好可以通過調用一個實例的實例方法來實現,那麼就可以使用實例方法引用
對象方法引用
抽象方法的第一個參數類型剛好是實例方法的類型(函數式接口的抽象方法必須要有輸入參數)抽象方法剩餘的參數恰好可以當做實例方法的參數
如果函數式接口的實現能由上面說的實例方法調用來實現的話,那麼就可以使用對象方法的引用(兩個條件都要滿足)
我們看到第一參數Prod s,和實例對象new Prod是同一個類型;剩餘的參數s1,正好是實例方法fun的參數;這樣就可以簡化成Prod::fun
構造方法引用
如果函數式接口的實現恰好可以通過調用一個類的構造方法來實現,那麼就可以使用構造方法引用,語法【類名::new】
上面是無參數的構造函數,再來看看有參構造函數
到這裡,老顧來個方法引用的總結:
總結
Lambda表達式要熟練掌握,是要小夥伴們經常去寫,才能夠運用自如,代碼雖然比較簡潔,但確實沒有接觸的開發人員看上去,就懵逼了,可讀性不強
--End--
最近老顧上傳了微服務網關的分享課程,請大家支持