Java8 函數式接口 , lambda表達式,接口默認方法

函數式接口:

當接口裏只有一個抽象方法的時候,就是函數式接口,可使用註解(@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只能用於函數式接口。

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表達式那行會報錯。

相關文章
相關標籤/搜索