如果你原本有個 Collection
或 Map
已收集了一些元素,現在打算傳遞這個物件,而且不希望拿到此物件的任何一方對它做出修改(Modify)操作,那麼可以使用 Collections
上提供的 unmodifiableXXX()
方法,那些方法會傳回一個不可修改的物件,如果單純只是取得元素是沒問題,如果呼叫了有副作用的 add()
、 remove()
等方法,則會丟出 UnsupportedOperationException
,例如:
jshell> List<String> names = new ArrayList<>();
names ==> []
jshell> names.add("Monica");
$2 ==> true
jshell> names.add("Justin");
$3 ==> true
jshell> List<String> unmodifiableNames = Collections.unmodifiableList(names);
unmodifiableNames ==> [Monica, Justin]
jshell> unmodifiableNames.get(0);
$5 ==> "Monica"
jshell> unmodifiableNames.add("Irene")
| java.lang.UnsupportedOperationException thrown:
| at Collections$UnmodifiableCollection.add (Collections.java:1056)
| at (#6:1)
那麼,透過 unmodifiableXXX()
方法傳回的物件是不可變嗎?不是,傳回的物件只是無法修改(Unmodifiable),也就是僅僅不支援修改操作罷了,這是什麼意思?以上面的程式片段來說,如果我直接呼叫 names.add("Irene")
,unmodifiableNames
的內容也就跟著變動了:
jshell> names.add("Irene");
$7 ==> true
jshell> unmodifiableNames;
unmodifiableNames ==> [Monica, Justin, Irene]
之所以會如此,是因為 get()
、containsAll()
這類方法,只是單純委託給 unmodifiableXXX()
接收之物件(而 add()
等方法直接撰寫 throw new UnsupportedOperationException()
),例如 unmodifiableCollection()
方法的實作是這樣的:
public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c) {
return new UnmodifiableCollection<>(c);
}
static class UnmodifiableCollection<E> implements Collection<E>, Serializable {
private static final long serialVersionUID = 1820017752578914078L;
final Collection<? extends E> c;
UnmodifiableCollection(Collection<? extends E> c) {
if (c==null)
throw new NullPointerException();
this.c = c;
}
略...
public boolean add(E e) {
throw new UnsupportedOperationException();
}
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
public boolean containsAll(Collection<?> coll) {
return c.containsAll(coll);
}
略...
}
不可變從來也沒在 Collections
上那些 unmodifiableXXX()
方法的保證中,畢竟名稱上也指出了,傳回的物件是不可修改,而不是不可變動。無論這是不是在玩文字遊戲,如果你要的是更進一步的不可變特性,那麼使用 Collections
的 unmodifiableXXX()
傳回的物件,顯然是有所不足。