要想說清楚JAVA中的Lambda表達式,必須想講一下匿名內部類來幫助理解本質。
一.匿名內部類
匿名內部類適合創建那種只需要一次使用的類,例如前面介紹命令模式時所需要的Command對象,匿名內部類的語法有點奇怪,創建匿名內部類時會立即創建一個該類的實例,這個類的定義立即消失,匿名內部類不能重復使用。
語法格式一般如下:
new 實現接口() | 父類構造器 (實參列表){
//匿名內部類的類體部分
}
從上面可以看出,匿名內部類必須繼承一個父類,或者實現一個接口,但最多只能繼承一個父類,或者實現一個接口。
關於匿名內部類還有如下兩條規則:
1.匿名內部類不能是抽象類,因為系統在創建匿名內部類時,會立即創建匿名內部類的對象,因此不允許將匿名內部類定義為抽象類。
2.匿名內部類不能定義構造器(Constructor),由於匿名內部類沒有類名,所以無法定義構造器,但匿名構造類可以定義初始化塊,可以通過實例初始化塊來完成構造函數需要完成的部分。
Example 1:
interface MyInter{ public String getName(); } public class AnonymousTest{ public void test(MyInter i){ System.out.println("The Content of this function is "+i.getName()); } public static void main(String[] args){ AnonymousTest ta; ta.test(new MyInter(){ public String getName(){ return "Anonymous Content"; } }); }
以上的代碼等價的實現類對象的代碼是:
class AnonymousEqual implements MyInter{ public String getName(){ return "Anonymous Class Content"; } }
Example 2:
abstract class MyUpperClass{ private String name; public abstract String getName(); public MyUpperClass(){} public MyUpperClass(String s){ name=s; } }////Abstract instead of Interface public class AnonymousTest{ public void test(MyUpperClass m){ System.out.println("The Content of This Method is "+m.getName()); } ////////////// public static void main(String[] args){ AnonymousTest at; at.test(new MyUpperClass("Abstract Class Derived Content"){ public String getName(){ return "AnonymousClass Derived Content"; } }); } }
二.Lambda表達式
Lambda表達式是Java8的重要更新,也是一個被廣大開發者期待已久的新特性。Lambda表達式支持將代碼塊作為參數,Lambda表達式允許使用更簡潔的代碼來創建只有一個抽象方法的接口(這種接口被稱為函數式接口)的實例。
Lambda表達式完全可以用於簡化創建匿名內部類對象,因此可將上面中的Example 1中的代碼改寫成如下的形式。
public class AnonymousTest{ public void test(MyInter i){ System.out.println("The Content of This Method is "+i.getName()); } ////////////////// public static void main(String []args){ AnonymousTest at; at.test(()->new String("Lambda Content")); } }
從上面的程序之中可以看出,這段代碼之中Lambda表達式所實現的test方法和匿名內部類所實現的test方法是完全等價的,只是不再需要一個繁瑣的代碼塊重新聲明一個匿名類,不需要重新指出所重寫的方法的名字,也不需要給出重寫的方法的返回值類型。
從上面的方法之中可以看出,lambda表達式代替匿名內部類的時候,lambda代碼塊將會實現代替實現抽象類的方法體,lambda表達式的語法主要由三部分構成:
(1)形參列表,如果只有一個參數可以省略括號,當無參數類型時可以使用()或者obj來代替。
(2)箭頭(->)
(3)代碼塊部分,如果代碼只有一行則可以省略掉花括號,不然使用花括號將lambda表達式的代碼部分標記出來。
Lambda表達式的類型,也被稱為“目標類型(target type)”,lambda表達式的目標類型必須是“函數式接口(functional interface)”。函數式接口代表只包含一個抽象方法的接口。函數式接口可以包含多個默認方法,類方法,但只能聲明一個抽象方法。如果采用匿名類型內部類來創建函數式接口的實例,則只需要實現一個抽象方法,在這種情況下即可采用lambda表達式來創建對象,該表達式創建出來的對象目標就是這個函數接口。(可以用@FunctionalInterface注解來對函數接口實行限制)
##表達式的目標類型必須是明確的函數式接口
##lambda表達式只能為函數式接口創建對象,lambda表達式只能實現一個方法,因此他它只能為只有一個抽象方法的借口(函數式接口)創建對象。
另外需要注意的一點是: Object不是函數式接口
為了保證lambda表達式的目標類型是一個明確的函數式接口,可以有如下三種常見的方法:
##將lambda表達式賦值給函數式接口類型的變量
##將lambda表達式當作參數傳遞給需要函數式接口類型的參數的調用方法
##使用函數式接口對lambda表達式進行強制的類型轉換
附:
在java.util.function包預定下了大量函數式接口,典型的包含如下4類接口。
***Function:這類接口通常包含一個apply抽象方法,對參數進行處理轉換,然后返回一個新的值。
***Consumer:這類接口通常包含一個accept抽象方法,用於對參數進行處理,但是不返回一個新的值。
***Predicate:這類接口通常包含一個test抽象方方法,通過對參數的處理計算,然后返回一個boolean值
***Supplier:這類接口通常包含一個getAs***抽象方法,這種方法無參數,按照某種邏輯運算返回一個數據值。
