簡體   English   中英

Java集合API:為什么Unmodifiable [List | Set | Map]不是公開可見的類?

[英]Java collections API: why are Unmodifiable[List|Set|Map] not publicly visible classes?

Collections.unmodifiableList(...)返回靜態內部類 UnmodifiableList的新實例。 其他不可修改的集合類以相同的方式構造。

如果公開這些課程,一個有兩個好處:

  • 能夠指示更具體的返回值(例如UnmodifiableList ),因此API用戶不會想到修改該集合;
  • 如果Listinstanceof UnmodifiableList則在運行時檢查的能力。

那么,是否有任何優勢不讓這些課程公開?

編輯 :沒有提出絕對令人信服的論據,所以我選擇了最受歡迎的答案。

我個人完全同意你的意見。 問題的核心是Java的泛型不是協變的,而這反過來又是因為Java的集合是可變的。

Java的類型系統不可能編寫一個似乎有mutator 實際上是不可變的類型 想象一下,如果我們開始設計一些解決方案:

interface Immutable //marker for immutability

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

但是ImmutableMapMap的子類,因此Map可以從ImmutableMap賦值,所以任何返回這種不可變Map的方法:

public ImmutableMap<K, V> foo();

可以分配給Map,因此可以在編譯時進行變異:

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

所以,你可以看到這種類型的添加實際上並沒有阻止我們做任何壞事。 我認為由於這個原因,我們判斷它沒有足夠的價格。


scala這樣的語言具有聲明站點方差注釋。 也就是說,您可以將類型指定為協變(因此不可變),因為Scala的Map是(實際上它在V參數中是協變的)。 因此,您的API可以聲明其返回類型是可變的還是不可變的。

另外,Scala允許您聲明交集類型,以便您甚至不需要將ImmutableXYZ接口創建為單獨的實體,您可以指定要返回的方法:

def foo : XYZ with Immutable

但是scala有一個合適的類型系統,而Java沒有

我認為這兩個優點都有,但沒有那么有用。 主要問題保持不變:UnmodifiableList仍然是List,因此所有setter都可用,底層集合仍然可以修改。 使類UnmodifiableList公開會增加不可修改的錯覺。

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

缺點是在API中引入至少三個附加類/接口。 由於它們沒有那么有用,我認為將它們排除在API之外是一個不錯的選擇。

如果您需要檢查列表是否是使用Collections.unmodifiableList創建的,那么您可以創建一個實例並請求該類。 現在您可以將此類與任何列表的類進行比較。

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

答案的原因很簡單:當時,1998年,高效的設計有點側面。 人們想到這一點並不是一個優先事項。 但是沒有真正的,深刻的思考。

如果你想使用這樣的機制,使用Guava的ImmutableList / Set / Map / ...

它們是顯式不可變的,並且使用該庫時的一個好習慣不是返回一個List,例如一個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
    ...
  }
  ...
}
  • 能夠指示更具體的返回值(例如UnmodifiableList ),因此API用戶不會想到修改該集合;

在適當的API中,這應該已經記錄在返回不可修改列表的方法的javadoc中。

  • 如果Listinstanceof UnmodifiableList則在運行時檢查的能力。

這種需要表明實際問題存在於其他地方。 這是代碼設計中的一個缺陷。 問問自己,您是否曾經需要檢查List是否是ArrayListLinkedList的實例? 無論是ArrayListLinkedList還是UnmodifiableList ,顯然都是在代碼寫入時間內做出的決定,而不是在代碼運行時。 如果您遇到問題,因為您正在嘗試修改UnmodifiableList (API開發人員可能已經記錄了非常好的版本),那么這是您自己的錯,而不是運行時錯誤。

總而言之,沒有任何意義。 Collections#unmodifiableXXX() checkedXXX() Collections#unmodifiableXXX()synchronizedXXX()checkedXXX()以任何方式執行並不代表具體實現。 它們都只是裝飾器 ,無論底層具體實現如何都可以應用。

假設UnmodifiableList 一個公共類。 我懷疑它會讓程序員陷入虛假的安全感。 請記住, UnmodifiableList可修改 List視圖 這意味着UnmodifiableList的內容仍然可以通過對其基礎List進行的任何更改來更改。 一個天真的程序員可能無法理解這種細微差別,並且可能期望UnmodifiableList實例是不可變的。

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

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

暫無
暫無

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

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