簡體   English   中英

為什么Java中的許多Collection類都會擴展抽象類並實現接口?

[英]Why do many Collection classes in Java extend the abstract class and implement the interface as well?

為什么Java中的許多Collection類都擴展Abstract類並實現接口(也由給定的Abstract類實現)?

例如,類HashSet擴展了AbstractSet並實現了Set ,但是AbstractSet已經實現了Set

這是記住此類真正實現該接口的一種方式。
它不會有任何不良影響,並且可以幫助您理解代碼,而無需遍歷給定類的完整層次結構。

從類型系統的角度來看,如果類沒有再次實現該接口,它們將沒有任何不同,因為抽象基類已經實現了它們。

那是真的。

他們反正實現它的原因是(可能)主要文件:一個HashSet是,一個Set 通過添加implements Set到末尾,這是明確的,盡管這不是絕對必要的。

請注意,使用反射實際上可以觀察到這種差異,但是如果HashSet不直接實現Set ,我將很難產生一些會中斷的代碼。

在實踐中,這可能無關緊要,但是我想澄清一下,顯式實現接口與通過繼承實現接口並不完全相同 差異存在於編譯的類文件中,並且可以通過反射看到。 例如,

for (Class<?> c : ArrayList.class.getInterfaces())
    System.out.println(c);

輸出僅顯示由ArrayList 顯式實現的接口,按在源中編寫的順序排列(在我的Java版本中)為:

interface java.util.List
interface java.util.RandomAccess
interface java.lang.Cloneable
interface java.io.Serializable

輸出不包括由超類實現的接口,也不是作為所包含接口的超接口的接口。 特別是,即使ArrayList隱式實現了它們,上述內容中也缺少IterableCollection 要找到它們,您必須遞歸地迭代類層次結構。

不幸的是,那里的某些代碼使用反射並依賴於顯式實現的接口,但是這是可能的,因此collections庫的維護者可能不願意現在更改它,即使他們願意。 (有一個稱為Hyrum定律的觀察:“使用足夠多的API用戶,您在合同中承諾的內容並不重要;系統的所有可觀察到的行為將取決於某人”。)

幸運的是,這種差異不會影響類型系統。 new ArrayList<>() instanceof IterableIterable.class.isAssignableFrom(ArrayList.class) new ArrayList<>() instanceof Iterable表達式的計算結果仍為true

Colin Hebert不同的是,我不買那些關心可讀性的人。 (每個認為標准Java庫是由無可挑剔的神編寫的人,都應該看一下它們的源代碼。第一次這樣做時,我對代碼格式和眾多復制粘貼的塊感到震驚。)

我敢打賭,這已經很晚了,他們很累,一點也不在乎。

摘自Joshua Bloch的“有效Java”:

您可以通過添加抽象骨架實現類和接口一起來結合接口和抽象類的優點。

接口定義類型,可能提供一些默認方法,而骨架類在原始接口方法之上實現其余的非原始接口方法。 擴展骨架實現可以使大部分工作脫離實現接口。 這是模板方法模式。

按照慣例, 骨架實現類稱為AbstractInterface ,其中Interface是它們實現的接口的名稱。 例如:

AbstractCollection
AbstractSet
AbstractList
AbstractMap

我也相信這是為了清楚。 Java Collections框架具有相當的接口層次結構,用於定義不同類型的集合。 它從Collection接口開始,然后由三個主要子接口Set,List和Queue擴展。 還有SortedSet擴展Set和BlockingQueue擴展Queue。

現在,如果具體類明確聲明其正在實現的層次結構中的哪個接口,盡管具體類有時看起來可能很多余,但實現起來更容易理解。 如您所述,像HashSet這樣的類可以實現Set,但是像TreeSet這樣的類雖然可以擴展AbstractSet,但它可以實現SortedSet,而不僅僅是Set。 HashSet可能看起來很多余,但TreeSet卻不是,因為它需要實現SortedSet。 仍然,這兩個類都是具體的實現,並且如果它們在聲明中都遵循某些約定,則將更易於理解。

甚至有一些類實現了多個集合類型,例如LinkedList,它同時實現了List和Queue。 但是,PriorityQueue至少有一個類有點“非常規”。 它擴展了AbstractQueue,但未明確實現Queue。 不要問我為什么。 :)

(參考來自Java 5 API)

在我看來,當一個類實現一個接口時,它必須實現其中存在的所有方法(默認情況下,它們是接口中的公共方法和抽象方法)。

如果我們不想實現所有的接口方法,則它必須是一個抽象類。

因此在這里,如果某些方法已經在實現特定接口的抽象類中實現了某些方法,並且我們必須擴展尚未實現的其他方法的功能,那么我們將需要在類中再次實現原始接口以獲取其余的方法集。維護界面制定的合同規則。

如果僅實現接口,並且再次在我們的類中使用方法定義覆蓋所有方法,則將導致返工。

來不及回答?

我正在猜測以驗證我的答案。 假設以下代碼

HashMap extends AbstractMap (不實現Map)

AbstractMap implements Map

現在,想象一下一個隨機的家伙來了,Changed使用與Map完全相同的方法集將Map實現到某些java.util.Map1

在這種情況下,不會有任何編譯錯誤並且jdk會被編譯(當然測試會失敗並捕獲此錯誤)。

現在,任何將HashMap用作Map m = new HashMap()的客戶端都將開始失敗。 這在下游。

由於AbstractMap和Map等都來自同一產品,因此該參數看起來很幼稚(很可能是或可能不是),但請考慮一個項目,其中基類來自其他jar /第三方庫等。第三方/不同的團隊可以更改其基本實施方式。

同樣,通過在Child類中實現“接口”,開發人員試圖使該類自成體系,並證明API不會損壞。

我想,即使提供默認的操作實現並不能一勞永逸,也可能有其他方法來處理集合的成員(接口)。 循環隊列與LIFO隊列可能都實現相同的接口,但是它們的特定操作將以不同的方式實現,對嗎?

如果您只有一個抽象類,那么您將無法創建自己的類,而該類也將從另一個類繼承。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM