Java集合為什麼設計為:實現類繼承了抽象類,同時實現抽象類實現的介面

淵渟嶽發表於2022-01-08

 

更好閱讀體驗:Java集合為什麼設計為:實現類繼承了抽象類,同時實現抽象類實現的介面

問題

Java集合原始碼為什麼設計為:「實現類繼承了抽象類,同時實現抽象類實現的介面?」

看著List 集合的UML圖來分析

如圖:介面+抽象類都是成對出現,Collection 和 AbstractCollection;List 和 AbstractList。ArrayList 繼承了AbstractList,同時實現了List 介面。

img

再看下其他集合的UML 圖,看看是不是也是這樣設計的

img

img

img

這樣設計的意義

有的人說介面只能使用抽象方法,為了實現公共的方法抽出來用抽象類來實現,提高程式碼複用性,提高程式碼質量。

其實不是,第一句就錯了,介面同樣是可以實現方法,用的關鍵字是 default。如果真的是為了公共的實現方法抽出來用抽象類來實現,那還不如直接在介面上實現不更好,而且具體的實現類(如:ArrayList)還不用繼承抽象類,畢竟繼承還是單一的。

List 介面繼承了Collection 介面絕大部分方法,並新定義了List 特有的抽象方法,而這些特有的抽象方法都交給了AbstractList 來實現。

抽兩個方法來看看

    // AbstractList
    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }
​
    public E remove(int index) {
        throw new UnsupportedOperationException();
    }

在AbstractList中,這兩個方法只是拋了個“不支援的操作異常”,並沒有具體的內容。要明白抽象類是多種類的再一次抽象,也就是說具體的實現方法還是要針對不同的類來編寫,所以還要看看ArrayList 和 LinkedList 對add(int index, E element)的具體實現

 // ArrayList 
 public void add(int index, E element) {
        rangeCheckForAdd(index);
​
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1, size - index);
        elementData[index] = element;
        size++;
    }
​
 // LinkedList 
 public void add(int index, E element) {
        checkPositionIndex(index);
​
        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }

可見,這兩個方法的實現完全不同(底層資料結構不同),到此應該明白一點了:「List 介面定義個List 特有的規範,AbstractList 對這個規範做了通用實現,而具體實現還是要在具體類裡面落實」。但並不是只是為了高複用性、減少程式碼重複度。

比如:public int indexOf(Object o); 雖然抽象類實現了,但具體類還是重寫了這方法,顯然不是為了解決程式碼複用的問題,而是提供了通用實現,具體的還有看具體類使用了什麼型別的資料結構,然後在具體類中重寫方法定製化的實現。

    // AbstractList
   public int indexOf(Object o) {
        ListIterator<E> it = listIterator();
        if (o==null) {
            while (it.hasNext())
                if (it.next()==null)
                    return it.previousIndex();
        } else {
            while (it.hasNext())
                if (o.equals(it.next()))
                    return it.previousIndex();
        }
        return -1;
    }
    // ArrayList
    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
    // LinkedList
    public int indexOf(Object o) {
        int index = 0;
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;
                index++;
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }

意義

1.介面實現的解耦:介面新增或刪除方法時,只需在抽象類中做個方法初始實現(對於刪除方法可以不做處理),即可避免一個介面變動導致所有實現類都需要更新程式碼的問題。

2.多型特性的運用,向上轉型:ArrayList 、Vector、LinkedList 都可以實現對資料的儲存,都可以用List 作為引用(面向介面程式設計)。

「總結」

  • 提高介面的靈活性:介面只需要按需重寫方法;

  • 提高介面的易擴充套件:介面變動不影響實現類,只需要更改相應的抽象類實現即可;

  • 抽象類和介面的互補:介面規定行為規範,抽象類補充屬性和介面實現的解耦。

     

    個人理解,如有不對,還望糾正。

相關文章