![](/img/trans.png)
[英]How can we ensure the immutability of a ArrayList defined in a Immutable Object?
[英]Can we treat an object as immutable if we document its immutability
例如,我有這個:
public class Container{
private final List<String> strs;
/**
* Contructs {@code Container} by a given {@code List}
* The content of the list referenced should not be modified
* after the constructor invocation.
*/
public Container(List<String> strs){
this.strs = strs;
}
//Other staff
}
Container
的狀態可以在構建后修改,但我們禁止在文檔中。 該對象可以被視為不可變的嗎? 在構造函數中進行復制並不是完全可取的。
不變性是針對線程安全的。
如果您不想復制列表,並且仍然希望它實際上是不可變的,那么您可以使用持久數據結構,例如Clojure中的 數據結構 。
在Clojure中,所有值都是不可變的,因此您始終可以安全地傳遞它們。 當有人將一個項目添加到列表的前面時,這會邏輯上創建一個新列表,但實際上它只添加一個指向舊列表頭部的元素,而不必復制所有以前的列表。 Clojure中的所有數據結構都是這樣的,您也可以在Java中使用它們(另請參閱什么是在java中使用的好的持久性集合框架? )。
或者,您可以使用pcollections 。
通常,您不應將類的內部結構暴露給外部。 這對於imutables來說非常重要。
因此,我建議在構造函數中使用new ArrayList(x)
創建一個副本。
此外,您可以使用Collections.unmodifiableList()
來防止從類內部進行修改。
我建議將兩者結合起來,看起來像這樣:
import java.util.Collections;
...
public Container(List<String> strs){
this.strs = Collections.unmodifiableList(new ArrayList<>(strs));
}
您的容器將在構造函數調用時記住列表的成員。 如果列表在其他地方更改,它將不會對您的imutable產生任何影響。
即使容器中的代碼也無法修改列表 - 該列表會拋出UnsupportedOperationException
。
完整的,有效的示例代碼:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class X {
public static void main(String[] args) {
// create a list
List<String> myList = new ArrayList<>();
myList.add("a");
myList.add("b");
// hand it over to the container
Container container = new Container(myList);
// modify it afterwards
myList.add("BUH!");
// check contents of container
for (String item : container.strs) {
System.out.println(item);
}
}
}
class Container{
final List<String> strs;
/**
* Contructs {@code Container} by a given {@code List}
* The content of the list referenced should not be modified
* after the constructor invokation.
*/
public Container(List<String> strs){
this.strs = Collections.unmodifiableList(new ArrayList<>(strs));
}
//Other staff
}
它輸出:
a
b
(不輸出BUH!
)
您可以只記錄它以提高性能,但我只建議使用非公共API的方法。 在我的公司中,我們構建了一個框架,並設法取消一些這些保護以提高性能/減少內存使用,但僅限於內部代碼。 我們決定假設框架開發人員不會亂七八糟,而且效果很好。
如果您的API將暴露給外部的其他開發人員,則應始終保持安全,特別是出於安全原因。 如果您確實需要提高性能/內存使用率,那么這是一個選項,但請謹慎應用。
您允許這種結構的決定是保證正確性和性能之間的權衡。
構建器模式可能會為您提供第三種處理方式(取決於它是否適用於您的列表構造):
要構造Container
創建一個Builder
,它收集列表元素,然后最終調用(私有)Container構造函數。 這樣可以方便地收集列表元素,並避免在構造最終對象時復制列表:
public class Container {
public static class Builder {
public Builder add(String s) { strs.add(s); return this; }
public Container create() {
Container c = new Container(Collections.unmodifiableList(strs));
strs = null;
return c;
}
private List<String> strs = new ArrayList<>();
}
private final List<String> strs;
private Container(List<String> strs){
this.strs = strs;
}
}
一般來說,沒有。 可以在線程之間安全地共享不可變對象。 在線程之間共享的安全對象不僅僅是文檔問題; 它的類必須以特定的方式實現。 說“這個類的對象是不可變的”沒有一個讓它變得不可變的實現會主動誤導,一個不真實,應該被視為一個bug。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.