當接口裏只有一個抽象方法的時候,就是函數式接口,可使用註解(@FunctionalInterface)強制限定接口是函數式接口,即只能有一個抽象方法。java
例如:ide
package com.simple.lambdajava; /** * Created by zhubo on 2018/4/14. * 默認是函數式接口 */ public interface Interface1 { void method1(); }
上面的接口只有一個抽象方法,則默認是函數式接口。函數
interface Integerface2 { void test(); void test2(); }
該接口有兩個抽象方法,不是函數式接口測試
@FunctionalInterface interface Integerface2 { }
上面這樣寫編譯會報錯,由於@FunctionalInterface註解聲明瞭該接口是函數式接口,必須且只能有一個抽象方法。spa
@FunctionalInterface public interface Interface1 { void method1(); }
Lambda表達式只能針對函數式接口使用。code
從java8開始接口裏能夠有靜態方式,用static修飾,可是接口裏的靜態方法的修飾符只能是public,且默認是public。blog
package com.simple.lambdajava; /** * Created by zhubo on 2018/4/14. * 接口裏的靜態方法 */ public interface TestStaticMethod { static void test1() { System.out.println("接口裏的靜態方法!"); } }
使用接口類名調用靜態方法:繼承
/** * Created by zhubo on 2018/4/14. */ public class Test { public static void main(String[] args) { TestStaticMethod.test1(); } }
/** * Created by zhubo on 2018/4/14. * 接口裏的靜態方法 * 也是函數式接口 */ @FunctionalInterface public interface TestStaticMethod { // 這是一個抽象方法 void test(); // 靜態方法,不是抽象方法 static void test1() { System.out.println("接口裏的靜態方法!"); } }
上面代碼並不會報錯,能夠看到接口仍然是函數式接口接口
java8裏,除了能夠在接口裏寫靜態方法,還能夠寫非靜態方法,可是必須用default修飾,且只能是public,默認也是public。字符串
//非靜態default方法 interface TestDefaultMethod{ default void test() { System.out.println("這個是接口裏的default方法test"); } public default void test1() { System.out.println("這個是接口裏的default方法test1"); } //編譯報錯 // private default void test2() { // System.out.println("這個是接口裏的default方法"); // } }
因爲不是靜態方法,因此必須實例化才能夠調用。
public class Test { public static void main(String[] args) { //使用匿名內部類初始化實例 TestDefaultMethod tx = new TestDefaultMethod() { }; tx.test(); tx.test1(); } }
默認方法能夠被繼承。可是要注意,若是繼承了兩個接口裏面的默認方法同樣的話,那麼必須重寫。
interface A { default void test() { System.out.println("接口A的默認方法"); } } interface B { default void test() { System.out.println("接口B的默認方法"); } } interface C extends A,B { }
這裏接口c處會報錯,由於編譯器並不知道你到底繼承的是A的默認方法還說B的默認方法。能夠修改以下進行重寫,用super明確調用哪一個接口的方法:
interface C extends A,B { @Override default void test() { A.super.test(); } }
測試:
public class Test { public static void main(String[] args) { C c = new C() { }; c.test(); } }
類繼承兩個有一樣默認方法的接口也是同樣,必須重寫。
下面的代碼編譯會報錯
class D implements A,B { void test() { } }
由於A或B的test方法是默認方法,修飾符爲public,重寫該方法修飾符必須等於或者大於它,而public已是最大的訪問修飾符,因此這裏修飾符必須是public
class D implements A,B { @Override public void test() { A.super.test(); } }
public static void main(String[] args) { D d = new D(); d.test(); }
注意:默認方法並非抽象方法,因此下面這個接口仍然是函數式接口。
@FunctionalInterface interface A { default void test() { System.out.println("接口A的默認方法"); } void test1(); }
在接口裏可使用默認方法來實現父接口的抽象方法。如:
interface C extends A,B { @Override default void test() { A.super.test(); } default void test1() { System.out.println("在子接口實現父接口的抽象方法"); } }
C c = new C() { }; c.test1();
在實際使用匿名函數調用時能夠重寫:
C c = new C() { @Override public void test1() { System.out.println("調用時重寫"); } }; c.test1();
能夠在子接口裏重寫父接口的默認方法,使其成爲抽象方法。
interface E { default void test() { System.out.println("接口E的默認方法"); } } interface F extends E { void test(); }
下面main方法裏這樣寫不會報錯
E e = new E(){ }; e.test();
但若是是這樣:
F f = new F(){ }; f.test();
則編譯報錯,要求你必須實現test()方法:
能夠改成:
public static void main(String[] args) { F f = new F(){ @Override public void test() { System.out.println("F接口實現"); } }; f.test(); }
能夠認爲是一種特殊的匿名內部類
lambda只能用於函數式接口。
lambda語法:
([形參列表,不帶數據類型])-> {
//執行語句
[return..;]
}
注意:
一、若是形參列表是空的,只須要保留()便可
二、若是沒有返回值。只須要在{}寫執行語句便可
三、若是接口的抽象方法只有一個形參,()能夠省略,只須要參數的名稱便可
四、若是執行語句只有一行,能夠省略{},可是若是有返回值時,狀況特殊。
五、若是函數式接口的方法有返回值,必須給定返回值,若是執行語句只有一句,還能夠簡寫,即省去大括號和return以及最後的;號。
六、形參列表的數據類型會自動推斷,只須要參數名稱。
public class TestLambda { public static void main(String[] args) { TestLanmdaInterface1 t1 = new TestLanmdaInterface1() { @Override public void test() { System.out.println("使用匿名內部類"); } }; //與上面的匿名內部類執行效果同樣 //右邊的類型會自動根據左邊的類型進行判斷 TestLanmdaInterface1 t2 = () -> { System.out.println("使用lanbda"); }; t1.test(); t2.test(); //若是執行語句只有一行,能夠省略大括號 TestLanmdaInterface1 t3 = () -> System.out.println("省略執行語句大括號,使用lanbda"); t3.test(); TestLanmdaInterface2 t4 = (s) -> System.out.println("使用lanbda表達式,帶1個參數,參數爲:"+s); t4.test("字符串參數1"); TestLanmdaInterface2 t5 = s -> System.out.println("使用lanbda表達式,只帶1個參數,可省略參數的圓括號,參數爲:"+s); t5.test("字符串參數2"); TestLanmdaInterface3 t6 = (s,i) -> System.out.println("使用lanbda表達式,帶兩個參數,不能夠省略圓括號,參數爲:"+s+" "+ i); t6.test("字符串參數3",50); } } @FunctionalInterface interface TestLanmdaInterface1 { //不帶參數的抽象方法 void test(); } @FunctionalInterface interface TestLanmdaInterface2 { //帶參數的抽象方法 void test(String str); } @FunctionalInterface interface TestLanmdaInterface3 { //帶多個參數的抽象方法 void test(String str,int num); }
package com.Howard.test12; public class CloseDoor { public void doClose(Closeable c) { System.out.println(c); c.close(); } public static void main(String[] args) { CloseDoor cd = new CloseDoor(); cd.doClose(new Closeable() { @Override public void close() { System.out.println("使用匿名內部類實現"); } }); cd.doClose( () -> System.out.println("使用lambda表達式實現")); } } @FunctionalInterface interface Closeable { void close(); }
能夠看出,lambda表達式和匿名內部類並不徹底相同
觀察生成的class文件能夠看出,lambda表達式並不會生成額外的.class文件,而匿名內部類會生成CloseDoor$1.class
和匿名內部類同樣,若是訪問局部變量,要求局部變量必須是final,若是沒有加final,會自動加上。
例如:
public class TestLambdaReturn { void re(LambdaReturn lr) { int i = lr.test(); System.out.println("lambda表達式返回值是:"+i); } public static void main(String[] args) { int i = 1000; tlr.re( () -> i); } } interface LambdaReturn { int test(); } 若是隻是上面那樣寫,編譯不會報錯,可是若是改成: public static void main(String[] args) { int i = 1000; tlr.re( () -> i); //報錯 i = 10; }
把i看成非final變量用,則lambda表達式那行會報錯。