Java8新特性系列-預設方法和靜態方法

Java8新特性系列-預設方法和靜態方法

在Java 8之前,預設情況下,介面中的所有方法都是公共的和抽象的。但是這一限制在Java 8中被打破了,Java 8允許開發人員在介面中新增新方法,而無需在實現這些介面的類中進行任何更改。

為什麼會有預設方法?

主要是為了方便擴充套件已有介面;如果沒有預設方法,假如給Java中的某個介面新增一個新的抽象方法,那麼所有實現了該介面的類都得修改,影響將非常大。

舉個例子,

Sortable

介面以及實現該介面的類

SortableNumberCollection

SortableStringCollection

。該介面有兩種方法:

void sort()

; 和

T peek()

public interface Sortable {    void sort();    T peek();}

sort()

方法用於物件排序,

T peek()

用於獲取指定元素,另外需要一個比較器類

ObjectComparator

來對物件進行排序。

public class ObjectComparator implements Comparator {    @Override    public int compare(Comparable o1, Comparable o2) {        return o1。compareTo(o2);   }}

SortableStringCollection

是一個自定義集合類可以進行排序,並檢視字串指定元素,程式碼如下:

public class SortableStringCollection implements Sortable {    private List items = new ArrayList<>();    public void add(String item) {        items。add(item);   }    @Override    public void sort() {        items。sort(new ObjectComparator());   }    @Override    public String peek() {        return items。get(0);   }}

同樣,

SortableNumberCollection

是一個自定義集合類,其中包含可以使用介面方法進行排序和檢視的數字列表指定元素,程式碼如下:

public class SortableNumberCollection implements Sortable {    private List items = new ArrayList<>();    public void add(Integer item) {        items。add(item);   }    @Override    public void sort() {        items。sort(new ObjectComparator());   }    @Override    public Integer peek() {        return items。get(0);   }}

在Java8之前如果對介面

Sortable

新增新方法:

T sortAndPeek()

,那麼

SortableStringCollection

SortableNumberCollection

都必須實現

T sortAndPeek()

方法。

Java8之後提供了一種新的實現方式,預設方法

default method

,我們可以對

Sortable

進行如下改造:

public interface Sortable {    void sort();    T peek();    default T sortAndPeek(){    // New ‘default method’ added in the interface        sort();        return peek();   }}

同時

SortableStringCollection

SortableNumberCollection

類不需要任何更改。這樣可以減少我們對原有程式碼的改動。同時如果需要,還可以在實現此介面的任何類中重寫該方法

T sortAndPeek()

的預設實現。

在下圖中我們看到

default Method

不通的標識:

Java8新特性系列-預設方法和靜態方法

在多繼承中使用預設方法問題

如果兩個或多個介面具有相同的預設方法簽名,並且一個類實現了這兩個介面,則將引發編譯時錯誤。例如:

public interface Interface1 {    void methodOne(String str);    default void newMethod(){        System。out。println(“Interface1: Newly added method”);   }}public interface Interface2 {    void methodTwo(String str);    default void newMethod(){        System。out。println(“Interface2: Newly added method”);   }}public class InterfaceImplementation implements Interface1, Interface2{    @Override    public void methodOne(String str) {        System。out。println(“Overridden methodOne: ” + str);   }    @Override    public void methodTwo(String str) {        System。out。println(“Overridden methodTwo: ” + str );   }}

此時程式碼會提示如下異常:

InterfaceImplementation inherits unrelated defaults for newMethod() from types Interface1 and Interface2

要解決此問題,我們將必須重寫類

InterfaceImplementation

中的方法:

public class InterfaceImplementation implements Interface1, Interface2{    @Override    public void methodOne(String str) {        System。out。println(“Overridden methodOne: ” + str);   }    // newMethod implemented to resolve the conflict。    @Override    public void newMethod() {        System。out。println(“InterfaceImplementation: Newly added method”);   }    @Override    public void methodTwo(String str) {        System。out。println(“Overridden methodTwo: ” + str );   }}

我們總結一下:

類中的方法優先順序最高。類或父類中宣告的方法的優先順序高於任何宣告為預設方法的優先順序。

如果無法依據第一條進行判斷,那麼子介面的優先順序更高:函式簽名相同時,優先選擇擁有最具體實現的預設方法的介面,即如果B繼承了A,那麼B就比A更加具體。

最後,如果還是無法判斷,繼承了多個介面的類必須透過顯式覆蓋和呼叫期望的方法,顯式地選擇使用哪一個預設方法的實現。

在Java 8中新增靜態方法

介面定義的靜態方法獨立於任何物件呼叫。所以,在呼叫靜態方法時,不需要實現介面,也不需要介面的例項,

就像“預設方法”一樣,“靜態方法”也可以新增到介面中。例如,我們可以新增一個靜態方法

Direction getDefaultDirection()

,該方法將返回預設

Direction

,例如:

public interface Sortable {    Direction defaultDirection = Direction。DESC;    enum Direction {        ASC,        DESC   };    void sort();    T peek();    static Direction getDefaultDirection(){    // ‘static method’ added to the interface。        return defaultDirection;   }}

在上面的示例中,可以使用類引用來呼叫靜態Direction getDefaultDirection()方法:

Sortable。getDefaultDirection()

對預設方法和靜態方法的一點思考

介面是設計模式中一種開閉原則的體驗,而java8賦予了介面新的特性,使得介面使用起來更加的得心應手了,這也有助於我們更加內聚自己的程式碼結構了。Java原始碼中也有很多場景使用到了預設方法,例如:

Iterator

介面,我們在開發中可以多使用一些新的特性從而提高開發效率及增加程式碼的健壯性。

public interface Iterable {    Iterator iterator();        default void forEach(Consumer<? super T> action) {        Objects。requireNonNull(action);        for (T t : this) {            action。accept(t);       }   }        default Spliterator spliterator() {        return Spliterators。spliteratorUnknownSize(iterator(), 0);   }    }