Java8 新特性,打破你對介面的認知

dayang9發表於2022-04-17

Java 8 之前,介面裡面只能寫抽象方法,不能寫實現方法

Java 8 開始是可以有方法實現的,可以在介面中新增預設方法和靜態方法

預設方法用 default 修飾,只能用在介面中,靜態方法用 static 修飾,並且介面中的預設方法、靜態方法可以同時有多個。

為什麼要用介面預設方法

舉一個很現實的例子:

我們的介面老早就寫好了,後面因為各種業務問題,避免不了要修改介面。

在 Java 8 之前,比如要在一個介面中新增一個抽象方法,那所有的介面實現類都要去實現這個方法,不然就會編譯錯誤,

而某些實現類根本就不需要實現這個方法也被迫要寫一個空實現,改動會非常大。

所以,介面預設方法就是為了解決這個問題,只要在一個介面新增了一個預設方法,所有的實現類就自動繼承,不需要改動任何實現類,也不會影響業務;

另外,介面預設方法可以被介面實現類重寫。

為什麼要有介面靜態方法?

介面靜態方法和預設方法類似,只是介面靜態方法不可以被介面實現類重寫。

介面靜態方法只可以直接通過靜態方法所在的 介面名.靜態方法名 來呼叫。

介面預設方法多繼承衝突問題

因為介面預設方法可以被繼承並重寫,如果繼承的多個介面都存在相同的預設方法,那就存在衝突問題。

衝突一

interface People {
    default void eat() {
        System.out.println("人吃飯。");
    }
}

interface Man {
    default void eat() {
        System.out.println("男人吃飯。");
    }
}

//Boy 同時繼承了 People 和 Man,此時在 IDEA 編輯器中就會報錯
interface Boy extends People, Man {

}

//這就是介面多繼承帶來的衝突問題,Boy 不知道該繼承誰的,
//這顯然也是個問題,IDEA 也會提示,需要重寫這個方法才能解決問題:
interface Boy extends People, Man {

    @Override
    default void eat() {
        People.super.eat();	//在方法裡面還能直接呼叫指定父介面的預設方法
        Man.super.eat();	//在方法裡面還能直接呼叫指定父介面的預設方法
        System.out.println("男孩吃飯。");
    }
}

用實現類測試一下

class Student implements Boy {
    public static void main(String[] args) {
        Student student = new Student();
        student.eat();
    }
}

//result
//人吃飯。
//男人吃飯。
//男孩吃飯。

衝突二

我們再換一種寫法,把 Man 繼承 People,然後 Man 重寫 People 中的預設方法

此時,編輯器不報錯了,而 People 的預設方法置灰了,提示沒有被用到。

再執行一下上面的示例,輸出:

interface People {
    default void eat() {
        System.out.println("人吃飯。");
    }
}

interface Man extends People{
    default void eat() {
        System.out.println("男人吃飯。");
    }
}

interface Boy extends People, Man {
    
}

//result
//男人吃飯

因為 Man 繼承 PeopleMan 又重定了預設方法。很顯然,這個時候,Boy 知道該繼承誰的預設方法了。

注意,此時,如果用 People 通過 super 呼叫父類的 eat 方法會報錯,因為調不到了

interface Boy extends People, Man {
    @Override
    default void eat() {
        People.super.eat();	//People報錯
        System.out.println("男孩吃飯");
    }
}

衝突三

Man 介面中新增一個方法:say,然後在 Boy 介面中新增一個預設方法:say

interface Man extends People{
    default void eat() {
        System.out.println("男人吃飯。");
    }
    void say();	//IDEA提示,say方法變灰,沒有被使用
}

這時候,Man 中的抽象方法居然被忽略了,IDEA 都提示說沒用到,這顯然是預設方法優先於抽象方法。

總結

介紹了 Java 8 的預設方法靜態方法,以及預設方法的衝突問題解決方案。

如果以後還說介面不能寫實現方法,那就太OUT了。

相關文章