在java8中,增長了函數接口、Lambda和方法引用,使得建立函數對象變得很容易高效,本文經過情景引入,具體說明一下使用Lambda表達式建立接口對象是如何提升編程效率的。固然文中的所涉及的並不是Lambda的所有使用場景。java
咱們已有共識:Java是面向對象的編程語言,若是咱們調用任何一個對象的方法,並往方法中傳遞參數,參數必須是類或接口的對象或者是基本類型變量。
按照這個共識,咱們看下面的代碼:編程
//定義一個接口 interface Example{ //接口中只定義了一個抽象方法 void add(Integer a,Integer b); } //接口的實現類 class ExampleImpl implements Example { @Override public void add(Integer a, Integer b) { System.out.println("a+b=" + (a + b)); } } //測試類 public class TestLambda { //測試類中定義了一個方法,並規定了接收參數的類型 public void test(Example example) { example.add(1, 1); } public static void main(String[] args) { //聲明一個接口類型的變量並經過接口實現類實例化 Example example = new ExampleImpl(); //main方法中調用本身類的其餘方法必須先建立該類的對象,經過對象調用 TestLambda testLambda = new TestLambda(); //調用test,並把實例好的對象傳給test testLambda.test(example); } }
本地運行,控制檯顯示結果爲:a+b=2。固然結果不重要。看懂了上述代碼,思考一下,代碼能否精簡,提升執行效率。
觀察得出:設計模式
接下來在上面的代碼基礎上,咱們使用匿名類替換實現類,使得代碼進一步精簡,代碼以下:編程語言
//定義一個接口 interface Example{ //接口中只定義了一個抽象方法 void add(Integer a,Integer b); } //去掉了接口的實現類 //測試類 public class TestLambda { //測試類中定義了一個方法,並規定了接收參數的類型 public void test(Example example) { example.add(1, 1); } public static void main(String[] args) { //main方法中調用本身類的其餘方法必須先建立該類的對象,經過對象調用 TestLambda testLambda = new TestLambda(); //調用test,並把Example類型的匿名類對象傳給方法 testLambda.test(new Example(){ @Override public void add(Integer a, Integer b) { System.out.println("a+b=" + (a + b)); } }); } }
使用匿名類,咱們精簡了代碼,還能不能更加精簡呢?
匿名類知足了傳統面向對象的設計模式對函數對象的需求,可是,匿名類的繁瑣使得Java中進行函數編程的前景變得十分黯淡。咱們平時的方法函數如同簡單函數f(x),像這種f(t(x))的複合函數不常見。所謂函數式編程就是將函數(一段操做)做爲一個基本單位進行傳遞。之前的Java中參數只能是具體的變量,函數式編程打破這一規範,能夠將整個方法做爲一個參數傳遞。
下面咱們使用Lambda表達式,進一步精簡代碼:ide
//定義一個接口 interface Example{ //接口中只定義了一個抽象方法 void add(Integer a,Integer b); } //去掉了接口的實現類 //測試類 public class TestLambda { //測試類中定義了一個方法,並規定了接收參數的類型 public void test(Example example) { example.add(1, 1); } public static void main(String[] args) { //main方法中調用本身類的其餘方法必須先建立該類的對象,經過對象調用 TestLambda testLambda = new TestLambda(); //調用test,並把Lambda表達式傳給方法 testLambda.test((a,b)->System.out.println("a+b=" + (a + b))); } }
Lambda表達式代碼:(a,b)->System.out.println("a+b=" + (a + b))
最終執行效果同上。這樣咱們經過使用Lambda表達式達到了代碼乾淨簡潔,執行效率高效的目的。函數式編程
咱們前後經過匿名類和Lambda表達式對代碼重構,使得代碼精簡,使用匿名類容易理解,可是使用Lambda表達式感受與咱們開頭的共識向左,緣由是什麼呢?這個表達式難道就是符合類型要求的對象?這個表達式到底是如何起做用的?我想你們必定存在不少疑問。
注意觀察:定義的接口只有一個抽象方法。
疑問答案:在Java8中(向後兼容),造成了「帶有單個抽象方法的接口是特殊的,值得特殊對待」的觀念,這些接口被稱爲函數接口,Java容許利用Lambda表達式建立這些接口的實例。Lambda相似於匿名類的函數,可是比它簡潔更多。Lambda表達式被稱爲函數對象。
類型推導:編譯器利用一個被稱爲類型推導的過程,根據上下文推斷出類型。首先代碼案例中,testLambda.test()方法惟一(TestLambda類中沒有重載的test方法),編譯器會強制認爲testLambda.test()方法中的實際參數類型即Lambda表達式類型爲定義的形式參數類型Example,又因爲接口只有一個抽象方法,Lambda表達式天然只能實現該抽象方法,Lambda表達式表達式中的參數類型天然就是接口中抽象方法的參數類型。函數
能寫成Lambda的接口的條件:接口中有且僅有一個抽象方法。如本案例中的接口Example,若是出現多個抽象方法,編譯器的類型推導會出錯。測試
函數式接口可使用(不強求)註解@FunctionalInterface 進行強制規範
更多Lambda語法知識,可參考官方文檔。spa