java8之後的介面

mpsky發表於2021-09-09

java8引入了stream。支援集合物件轉化成為stream。集合的介面上增加了一個方法。

    default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }

我們可以看到集合的介面上。增加了一個實現。
這個就是java8帶來的一個特性。介面支援了default關鍵字。
這樣帶來了什麼好處呢?

default的好處

我們嘗試如果沒有default,我們要如何實現。

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

這個是arraylist的實現。我們看到如果想給所有的核心類庫加。似乎我們只要collection加上介面。然後再AbstractList實現就可以了,這樣核心類庫的list就可以了。同理還有map

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {

如果是第三方的實現呢,例如自己為了區別,直接實現了List或者直接實現了collection。這樣的程式碼最可怕了,需要我們一個一個加上方法,才能編譯過去,而且實現的內容大同小異。
第三方實現的時候,可能沒有核心類庫這麼強大,沒有做中間的抽象類做區分,實現起來可能就極其複雜。
最麻煩的是,原來6寫出來的程式碼,編譯出的class檔案無法直接跑在8上。嚴重影響升級版本的。
直接在介面上加一個實現,直接避免了這個問題,使用者的code基本是不要改變的。

default的壞處

上面從相容性,開發等角度,講了default如果沒有的話,可能帶來的問題。
在8之前,介面和抽象類是有明確的分工的。

  • 介面是用來定義規範。
  • 抽象類是用來複用程式碼。

根據這樣的設計規則,我們設計code會更有章法。抽象類一出,大家的關注點都在protected方法和final方法。這個一般意味著,重點聚焦在繼承和抽象層面的規範化。
按照這個角度,看stream方法應該是設計在抽象類上的。並且設計成final。spliterator是一個protected方法,希望子類繼承的時候,各自做各自的實現。
在介面中可以加方法實現,這個會導致原來的潛在約定失效。大家得仔細想想這個實現具體來自哪裡。

所以我們正常開發依舊會遵循介面和抽象類的區別,但是在改造系統上,我們會利用default關鍵字來讓改造更快。

菱形繼承問題

這個多層繼承本來是cpp的多繼承問題,現在java的介面也同時有了這個問題。原來的介面是沒有實現的。所以最終只有一份實現。但是現在不一樣了。

interface FA {
    default void say() {
        System.out.println("FA");
    }

    ;
}
interface FA1 extends FA{
    
}

這種繼承是沒有問題的。


interface FA {
    default void say() {
        System.out.println("FA");
    }

    ;
}


interface FB {
    default void say() {
        System.out.println("FB");
    }
}

interface FC extends FA, FB {

    @Override
    default void say() {

    }
}

這種情況,編譯器會要求你FC實現方法。

interface FA {
    default void say() {
        System.out.println("FA");
    }
}


inerface FB {
    default void say() {
        System.out.println("FB");
    }
}

interface FC extends FA, FB {

    @Override
    default void say() {
        FB.super.say();
    }
}

可以再實現中,主動的指定實現的到底是來自誰的方法。
這裡建議多層和菱形的情況下,雖然有預設規則,但還是最好手動指定一下實現的情況。因為預設的情況,依賴繼承順序。如果有一個稍加改動,預設的值就變了。但是編譯時還發現不了。所以最好都是明確指定,之後就不可動了,後面誰修改的時候,需要手動確認一次。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1806/viewspace-2826622/,如需轉載,請註明出處,否則將追究法律責任。

相關文章