簡體   English   中英

使用guava immutable集合作為方法參數和/或返回類型

[英]Use of guava immutable collection as method parameter and/or return type

我試圖確定ImmutableList的最佳實踐。 以下是一個簡單的例子,有助於解決我的問題:

例如:

public ImmutableCollection<Foo> getFooOne(ImmutableList<Foo> fooInput){ 
   //.. do some work
   ImmutableList<Foo> fooOther = // something generated during the code
   return fooOther;
}

public Collection<Foo> getFooTwo(List<Foo> fooInput){
   //.. do some work
   List<Foo> fooOther = // something generated during the code
   return ImmutableList.copyOf(fooOther);
}

public void doSomethingOne(){
  ImmutableCollection<Foo> myFoo = getFooOne(myList);
  ...
  someOtherMethod(myFoo);
}

public void doSomethingTwo(){
  Collection<Foo> myFoo = getFooOne(myList);
  ...
  someOtherMethod(myFoo);
}

我的問題:

  1. 哪個在應用程序中最有意義? [doSomethingOne和getFooOne]或[doSomethingTwo和fooTwo]? 換句話說,如果你知道你正在使用ImmutableCollections,那么繼續來回和執行copyOf(),或者只是在各處使用Immutable是有意義的嗎?

  2. 這些示例是公共方法,可能意味着其他人使用它們。 如果這些方法是私有的並且在內部使用,那么這些答案會改變嗎?

  3. 如果用戶嘗試向不可變List添加任何內容,則將拋出異常。 因為他們可能沒有意識到這一點,顯然返回一個ImmutableCollection而不是Collection會更有意義嗎?

通常,明智的做法是不在已聲明的返回類型中提交特定實現,但我們將不可變類型視為異常。 聲明返回類型為Immutable*有幾個原因:

  • 他們記錄您正在返回快照,而不是實時視圖。
  • 他們記錄了調用者不能改變結果。
  • 他們記錄了保留了插入順序(在您的用例中可能會或可能不會很重要)。
  • 他們記錄了該集合不包含null
  • 有人可能想要asList()reverse()方法。
  • 如果他希望分配給一個Immutable*字段,你可以為某人保存一個copyOf()調用。 (但請注意,如果他確實包含copyOf() ,即使您沒有聲明返回類型,它也會為大多數不可變輸入短路。)

基本上,我只是來自https://github.com/google/guava/wiki/TenThingsAboutImmutableCollections ,您可能想要完整地查看。

如果我理解你的意圖,設計getFooXxx以制作一個可變 - 可變列表的不可變副本的正確方法是這樣的:

/**
 * Returns an <b>immutable copy</b> of input list.
 */
public ImmutableList<Foo> immutableCopyOfFoo(List<Foo> input){
    return ImmutableList.copyOf(input);
}

為什么?

  • 當給定列表是不可變的時, ImmutableList.copyOf() 發揮作用,
  • 方法簽名明確說明了它的作用,
  • 方法返回ImmutableList ,事實上,一個ImmutableCollection但為什么你想隱藏有關信息ImmutableList從用戶? 如果他想要,他會寫Iterable foo = immutableCopyOfFoo(mutableFoo); 相反,但99%他會使用ImmtableList,
  • 返回一個ImmutableList 做出了一個承諾 - “我是不變的,如果你試圖改變我,我會把所有事情搞砸!”
  • 最后但並非最不重要 - 建議的方法在內部使用是不必要的 ; 只是用

     someOtherMethod(ImmutableList.copyOf(foo)); 

    直接在你的代碼中......


您應該檢查@ChrisPovirk的答案 (並鏈接到該答案中的wiki)以了解即當List<Foo> input包含null時,如果您嘗試制作不可變副本,則會在運行時產生令人討厭的NPE ...


編輯回答評論#1:

Collection合同不如List的那么嚴格; 即,集合不保證任何元素的順序(“有些是有序的,有些是無序的”),而List則是(“有序集合(也稱為序列)”)。

如果輸入是List,則表明訂單很重要,因此輸出應保證相同。 設想:

public ImmutableCollection<Foo> immutableCopyOfFoo(List<Foo> input){
    return ImmutableSortedSet.copyOf(input, someFancyComparator);
}

它聞起來不對勁。 如果您不關心訂單,那么方法簽名可能是immutableCopyOfFoo(Collection<Foo> input) 但這取決於具體的用例。

我通常的做法是:

  • 接受參數列表(因此界面更易於客戶端使用)

  • 如果性能/內存使用/線程安全很重要,請將提供的List的內容復制到為您的類使用而優化的數據結構中

  • 當返回一個ImmutableList時,ImmutableList應該是返回類型(因為它給調用者更多關於它如何使用返回值的信息)

  • 當返回List的可變實現時,List應該是返回類型,除非返回類型的其他內容很重要(線程安全,作為一個壞*示例)

*這是一個糟糕的例子,因為如果您的返回值需要是線程安全的,那么可能意味着您的代碼出現了其他問題。

將List / ImmutableList替換為任何不可變集合類型。

public ImmutableCollection<Foo> getFooOne(ImmutableList<Foo> fooInput){
   ImmutableList<Foo> fooOther= fooInput;
   return ImmutableList.copyOf(fooOther);
}

這毫無意義。 你為什么要復制一個不可變的集合? 不變性的全部意義在於:它無法改變,所以你不妨重復使用它。

public Collection<Foo> getFooTwo(List<Foo> fooInput){
   ImmutableList<Foo> fooOther= ImmutableList.copyOf(fooInput);
   return ImmutableList.copyOf(fooOther);
}

??? 為什么兩次??? 這可以:

public Collection<Foo> getFooTwo(List<Foo> fooInput){
   return ImmutableList.copyOf(fooInput);
}

ImmutableList.copyOf(Collection)非常智能,可以ImmutableList修改ImmutableList ,並為其他所有內容創建一個新的ImmutableList

您應該始終在公共接口上使用標准JRE類。 在Guava的Immutable...類上沒有額外的方法,所以你沒有獲得任何編譯時的安全性:任何對這些對象進行更改的嘗試只會在運行時失敗(但請參閱Bart的評論)。 您應該在返回集合的方法中記錄它們是不可變的。

如果您擔心並發修改,則應該在公共方法上提供列表的防御性副本,但是可以在私有方法參數上指定ImmutableCollection

暫無
暫無

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

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