2010-11-28 48 views
20

Collections.unmodifiableList(...)返回靜態內部類的新實例UnmodifiableList。其他不可修改的集合類以相同的方式構建。Java集合API:爲什麼Unmodifiable [List | Set | Map]不是公開可見的類?

這些類市民,一個有兩個好處:

  • 指示一個更具體的返回值(如UnmodifiableList)的能力,所以一個API的用戶會不會來修改的想法採集;
  • 在運行期間檢查Listinstanceof UnmodifiableList的能力。

那麼,是否有任何優勢而不是使這些類公開?

編輯:沒有絕對有說服力的論據被提出,所以我選擇了最有回報的答案。

+3

爲什麼你需要做的是什麼? – 2010-11-28 11:58:51

+0

快!有人聯繫Joshua Bloch!我不知道他的電子郵件... – 2010-11-28 13:05:34

+0

約書亞布洛赫[有一個Twitter帳戶](http://twitter.com/joshbloch),任何人都知道如何操作Twitter ... – 2010-11-28 13:15:25

回答

3

我覺得這兩方面都有優勢,但沒有那麼有用。主要問題依然如此:UnmodifiableList仍然是一個List,因此所有setter都可用,並且底層集合仍然可以修改。使類UnmodifiableList公開會增加不可修改的錯覺。

更好的方法是讓編譯器幫忙,但爲此集合類層次結構必須改變很多。例如,Scala的收集API在這方面比較先進。

缺點是將至少三個額外的類/接口引入到API中。由於它們沒有那麼有用,我認爲將它們從API中排除是一個不錯的選擇。

0

假設UnmodifiableList公開課。我懷疑它會讓程序員陷入虛假的安全感。記住,UnmodifiableList視圖可修改的List。這意味着UnmodifiableList的內容仍可以通過對其基礎List所做的任何更改進行更改。一個天真的程序員可能不瞭解這種細微差別,並可能預期UnmodifiableList的例子是不可變的。

0

我認爲答案是因爲該方法形式正確地知道使用的泛型,並且不需要額外的編程來傳遞這些信息,而類表單需要更多的搞亂。 unmodifiableMap的方法形式有兩個浮點泛型參數,它將其映射到返回類型和傳遞參數的泛型參數。

public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) { 
    return new UnmodifiableMap<K,V>(m); 
} 
2

如果確認列表是否使用Collections.unmodifiableList創建,那麼您可以創建一個實例並請求該類,這一點很重要。現在你可以將這個類與任何列表的類進行比較。

private static Class UNMODIFIABLE_LIST_CLASS = 
    Collections.unmodifiableList(new ArrayList()).getClass(); 
... 
if(UNMODIFIABLE_LIST_CLASS == listToTest.getClass()){ 
    ... 
} 
6

我個人完全同意你的看法。問題的核心是Java的泛型不是協變的,這又是因爲Java的集合是可變的。

不可能對Java的類型系統編纂一類,似乎有存取器實際上不變。試想一下,如果我們開始設計一些解決方案:

interface Immutable //marker for immutability 

interface ImmutableMap<K, V> extends Map<K, V>, Immutable 

但隨後ImmutableMapMap一個子類,因此Map是分配從ImmutableMap所以返回這樣一個不變地圖的任何方法:

public ImmutableMap<K, V> foo(); 

可以被分配到Map並且因此可以在編譯時被突變:

Map<K, V> m = foo(); 
m.put(k, v); //oh dear 

因此,你可以看到,這種類型的添加實際上並沒有阻止我們做任何壞事。我認爲由於這個原因,有人認爲它沒有足夠的提供。


斯卡拉一種語言報關現場方差註解。也就是說,你可以指定一個類型爲協變(因而不可變),因爲Scala的Map(實際上它在V參數中是協變的)。因此你的API可以聲明它的返回類型是可變的還是不可變的。

至於另一一邊,斯卡拉可以聲明路口的類型,這樣你甚至不需要創建ImmutableXYZ接口作爲一個獨立的實體,您可以指定一個方法返回:

def foo : XYZ with Immutable 

但隨後階有一個適當的類型系統,而Java不

1
  • 能力表示更具體的返回值(如UnmodifiableList),這樣一個API的用戶會不會來修改科爾的想法ction;

在一個適當的API,這應該已經在返回不可修改的列表的方法的Javadoc文檔。

  • 能力在運行時檢查是否Listinstanceof UnmodifiableList

這種需要指出實際問題在於其他地方。這是代碼設計中的一個缺陷。問問自己,你有沒有必要檢查List是否是ArrayListLinkedList的實例?無論是ArrayList,LinkedList還是UnmodifiableList顯然都是在代碼寫入期間而不是代碼運行期間做出的決定。如果由於您試圖修改UnmodifiableList(API開發人員可能擁有應該已經記錄的非常好的內容)而遇到問題,那麼這是您自己的錯誤,而不是運行時錯誤。

所有這一切,都沒有任何意義。Collections#unmodifiableXXX(),synchronizedXXX()checkedXXX()以任何方式做不代表具體實現。它們全都只是decorators,無論具體的具體實現如何,都可以應用。

2

答案爲什麼很簡單:當時,在1998年,高效的設計有點棘手。人們認爲這並不是顯然的優先事項。但是沒有真正的,深刻的思考。

如果你想用這種機制,利用番石榴的ImmutableList /設置/地圖/ ...

他們是明確不變的,使用該庫時一個很好的做法是不返回一個列表的實例,但一ImmutableList。所以你會知道List/Set/Map/...是不可變的。

例子:

private final ImmutableList constants = ...; 
public final ImmutableList<String> getConstants() { 
    return constants; 
} 

關於設計本身UnmodifiableXxx的,一個可以做以下幾點:

public static final class UnmodifiableXxx implements Xxx { // don't allow extend 
    // static if inside Collections 
    UnmodifiableXxx (Xxx backend) { // don't allow direct instanciation 
    ... 
    } 
    ... 
}