2009-10-01 40 views
3

我的一個類有一個包含Set的字段。該字段只填充到構造函數中,然後由其他類讀取。本來我有這樣的事情:訪問Java中的私人收藏字段

public class Foo { 
    public final Set<String> myItems; 
    public Foo(Collection<String> theirItems) { 
     this.myItems = new LinkedHashSet<String>(theirItems); 
    } 
} 

但是,這違背了面向對象的最佳實踐,通過myItems應該是私有的,而且只能通過getter和setter方法訪問。於是我把它改爲:

public class Foo { 
    private final Set<String> myItems; 
    public Foo(Collection<String> theirItems) { 
     this.myItems = new LinkedHashSet<String>(theirItems); 
    } 
    public Set<String> getItems() { 
     return myItems; 
    } 
} 

現在myItems是私有的,但誰調用getItems()仍然可以添加/隨意刪除項目,這在本質上是相同的情況下,我收到了。 (我不是真正關心別人改變項目內容,這更是一個理論性的問題)

於是我改變了getItems()返回一個數組:

public String[] getItems() { 
    return myItems.toArray(new String[myItems.size()]); 
} 

現在我的項目是真正的私有。不幸的是,我知道讀取這些項目的對象實際上想要使用一個Set,所以它將不得不將這個數組轉換回來。我還可以返回myItems的副本:

public Set<String> getItems() { 
    return new LinkedHashSet<String>(myItems); 
} 

這給了他們想要的來電,但在創建每個接入一套新的。

你在這樣的情況下做什麼 - 不惜一切代價保護隱私,接受原始結構的轉換/複製,或犧牲對集合內容的控制,並依靠負責任的呼叫者?

回答

12

返回一個不可修改視圖到您的設置:

public Set<String> getItems() { 
    return Collections.unmodifiableSet(myItems); 
} 

需要注意的是,這意味着主叫方將仍然看到任何變化作出該集合,如果他們掛在返回的集合。如果你不想這樣做,你會複製...沒有(簡單)的方式。 (理論上,您可以創建一個不可修改的副本,並返回對該副本的引用,直到下次進行更改時爲止,但這會變得很混亂。)

重要的一點是要記錄您選擇的任何內容,以便調用者不會沒有任何令人討厭的驚喜。在很多方面,我認爲這實際上是大多數應用程序中的重要事情,其中​​調用者實際上並不是惡意的。只要清楚會產生什麼效果,在大多數情況下,防守並不那麼重要。當然,如果你的呼叫者可能是一些不值得信任的代碼,並且你的設置對安全等是至關重要的,那麼你處於不同的情況。

+0

@tster:我只是在編輯「文檔吧!當你評論:) – 2009-10-01 05:32:38

+0

哈哈,我剛剛刪除我的評論,因爲你評論。 – tster 2009-10-01 05:40:30

0

我會克隆集中返回它,這樣如果調用者修改了設置,它不會影響你自己的一套。

+0

當然,調用者也應該克隆收到的收集,以防萬一被叫忘記這麼做......勇敢的新世界=) – Zed 2009-10-01 05:37:15

1

我要和你最後的選擇去:

public Set<String> getItems() { 
    return new LinkedHashSet<String>(myItems); 
} 
8

這取決於上下文。有幾個選項:

  1. 返回集合。調用者可以根據需要修改該集合,但他們不能分配新的集合。這是最便宜的,但提供最少的保護。
  2. 返回的圖(使用Collections類的工廠)。調用者不能修改集合,但集合的更新對調用者可見。這通常相對便宜,因爲元素存儲未分配;只有一個包裝被創建。
  3. 使用相應集合的複製構造函數返回集合的快照。在這裏,調用者獲取集合的副本。他們可以修改副本,但原件未更新,並且原件的更新在副本中不可見。這是最昂貴的。
1

我要麼使集的副本,或者我用Collections.unmodifiableSet()。

如果性能是一個問題,我打破了封裝規則,並返回原設定。

3

如何「安全」你需要是什麼?你返回的數組引用了與你的集合相同的對象,如果這些對象有setter,那麼你允許調用者修改你的集合的內容......是嗎?

這將有可能克隆集合,或用於訪問該組提供一個不可變的接口。這是一個判斷呼籲。如果我正在開發框架代碼,我會傾向於在安全性和克隆方面犯錯。例如,當客戶更緊密地聯繫在一起時,我傾向於不太保守。