2012-03-24 157 views
115

可能重複:
Why should the interface for a Java class be prefered?多態性:爲什麼使用「List list = new ArrayList」而不是「ArrayList list = new ArrayList」?

什麼時候應該使用

List<Object> list = new ArrayList<Object>(); 

ArrayList繼承List,所以如果在ArrayList一些功能在List,然後我將失去的某些功能,對嗎?編譯器會在嘗試訪問這些方法時發現錯誤?

+0

哦。對不起。因爲我變成了'物體',所以我忘了它。 – hqt 2012-03-24 15:44:13

+6

@duffymo - 這個問題是問爲什麼'新列表()'不起作用。這是一個完全不同的問題。 – 2012-03-24 18:18:34

+2

它不是一個重複的問題。可能的重複有通用的問題,但具體的細節。我發現標題中的這個問題與實際要求的內容更相關/同步。 – Dexters 2013-05-26 20:31:53

回答

205

你要做到這一點的主要原因是將你的代碼從界面的特定實現中解耦出來。當你寫你這樣的代碼:

List list = new ArrayList(); 

你的代碼的其餘部分只知道數據是List類型,這是可取的,因爲它可以讓你的List接口輕鬆的不同實現之間進行切換。例如,假設你正在編寫一個相當大的第三方庫,並且說你決定用LinkedList來實現你的庫的核心。如果你的圖書館非常依賴訪問這些列表中的元素,那麼最終你會發現你做出了糟糕的設計決定;你會意識到你應該使用一個ArrayList(它給出O(1)訪問時間)而不是LinkedList(它給出O(n)訪問時間)。假設你已經編程到一個接口,做出這樣的改變很容易。你會簡單地改變實例的List從,

List list = new LinkedList(); 

List list = new ArrayList(); 

,你知道,這將工作,因爲你寫你的代碼遵循由List接口提供的合同。另一方面,如果你已經使用LinkedList list = new LinkedList()實現了庫的核心,那麼做出這樣的改變就不那麼容易了,因爲不能保證其餘的代碼不能使用方法具體到LinkedList類。總之,選擇只是設計問題......但這種設計非常重要(特別是在處理大型項目時),因爲它可以讓您在不中斷的情況下進行特定於實現的更改現有的代碼。

+4

明確的解釋。 :) – coder247 2012-03-28 07:55:42

+44

很多人沒有提及通過ArrayList list = new ArrayList()將它實例化爲局部變量是完全可以接受的。在方法簽名中使用List接口或作爲類級別變量的類型纔是真正有益的。誰在乎是否必須稍後再回來並更改本地方法聲明?將列表暴露給其他方法或庫等時,整個好處就來了。 – GreenieMeanie 2012-03-29 16:28:21

+5

這是真的,謝謝指出。事實上,現在我想到了,從客戶的角度而不是程序員自己來看,我可能更有意義。在這兩種情況下,使用'List list = new ArrayList()'是確保您不會破壞現有代碼的問題。 – 2012-04-01 17:05:42

63

這被稱爲編程接口。如果您希望將來轉移到List的某些其他實現,這將有所幫助。如果你需要ArrayList中的一些方法,那麼你需要編程到ArrayList a = new ArrayList()的實現。

6

這使你寫的東西,如:後來

void doSomething() { 
    List<String>list = new ArrayList<String>(); 
    //do something 
} 

,您可能希望將其更改爲:

void doSomething() { 
    List<String>list = new LinkedList<String>(); 
    //do something 
} 

,而無需改變該方法的其餘部分。

但是,如果你想使用一個CopyOnWriteArrayList例如,你需要將其申報爲這樣的,而不是作爲一個列表如果你想使用它的額外的方法(addIfAbsent爲例):

void doSomething() { 
    CopyOnWriteArrayList<String>list = new CopyOnWriteArrayList<String>(); 
    //do something, for example: 
    list.addIfAbsent("abc"); 
} 
+1

根據公認的答案@GreenieMeanie評論,這個改變並不重要,因爲這是一個局部變量。 – Ram 2016-01-07 09:37:12

5

只要我不想增加問題的複雜性,我就會使用該構造。這只是一個列表,不需要說明它是什麼樣的列表,因爲這對問題無關緊要。我經常對我的大部分解決方案使用Collection,因爲最後,在大多數情況下,對於其他軟件,真正重要的是它所包含的內容,而且我不想向Collection中添加新對象。

此外,當您認爲您可能想要更改正在使用的列表的實施時,您會使用該構造。假設您使用ArrayList構造,並且您的問題不是線程安全的。現在,您想讓線程安全,並且對於您的解決方案的一部分,例如,您可以更改爲使用Vector。至於這個列表的其他用途,如果它是一個AraryList或一個Vector,只是一個列表就不會有問題,不需要進行新的修改。

2

一般而言,您希望根據界面進行編程。這使您可以隨時交換實施。 這是非常有用的,特別是當你通過一個你不知道的實現。

但是,在某些情況下,您更願意使用具體的實現。 例如,當在GWT中序列化時。

17

這在暴露公共接口時也很有用。如果你有這樣的方法,

public ArrayList getList(); 

然後你決定將其改爲,

public LinkedList getList(); 

任何人誰在做ArrayList list = yourClass.getList()需要改變他們的代碼。另一方面,如果你這樣做,

public List getList(); 

更改實現不會改變任何API爲您的用戶。

+0

yourClass.getList()我猜是我碰到過的最實際的用例。非常感謝:) – 2017-05-05 16:38:52

4

我想你的問題的核心就是爲什麼program to an interface, not to an implementation

很簡單,因爲一個接口爲您提供了更抽象,並使代碼 更加靈活和有彈性的變化,因爲你可以使用相同的不同 實現接口(在這種情況下,您可能希望將List實現更改爲linkedList而不是ArrayList),而無需更改其客戶端。

10

我認爲@ tsatiz的答案大多是正確的(編程接口而不是實現)。但是,通過編程到接口您不會失去任何功能。讓我解釋。

如果您聲明變量爲List<type> list = new ArrayList<type>您實際上不會丟失ArrayList的任何任何功能。您所需要做的就是將您的list下降到ArrayList。這裏有一個例子:

List<String> list = new ArrayList<String>(); 
((ArrayList<String>) list).ensureCapacity(19); 

最後,我想tsatiz是正確的,因爲一旦你施放到一個ArrayList你不再編碼到一個接口。但是,最初編寫代碼到一個接口仍然是一個很好的做法,如果必須的話,如果它稍後有必要,則編碼到實現。

希望有幫助!

+0

這一個是非常容易和清晰。 – CopsOnRoad 2018-01-03 17:13:12

相關問題