List<String> list = Arrays.asList("a", "b"); list.add("c"); // 拋出 UnsupportedOperationException
很多開發者初次遇到此問題感到困惑。本文從源碼層面徹底講透原因。
Arrays.asList 入口@SafeVarargs
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a); // 注意:這不是 java.util.ArrayList!
}
Arrays.ArrayListprivate static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable {
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a; // 底層數組,final 不可變引用
ArrayList(E[] array) {
if (array == null) throw new NullPointerException();
a = array; // 直接引用傳入數組,無拷貝
}
public E get(int index) { return a[index]; }
public E set(int index, E element) {
E old = a[index];
a[index] = element;
return old;
}
// 沒有 add/remove 方法!
}
關鍵點:
AbstractList,而非 java.util.ArrayListfinal E[] a,長度固定get/set,無 add/removeAbstractList 的默認實現public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
這些「可選操作」默認拋異常。設計意圖:避免所有子類都要實現一堆無意義的方法。若改為抽象方法,每個子類都得重寫,極其繁瑣;採用「默認拋異常 + 文檔標註可選」,子類只需覆寫自己支持的操作。
asList 即「作為 List 視圖」,非「轉換為可變 List」List 接口包含可變/不可變操作,AbstractList 以「可選操作」模式平衡擴展性與簡潔性// 方式一:拷貝構造
List<String> mutable = new ArrayList<>(Arrays.asList("a", "b"));
mutable.add("c"); // OK
// 方式二:Java 9+ List.of + 拷貝
List<String> mutable = new ArrayList<>(List.of("a", "b"));
// 方式三:Stream 收集
List<String> mutable = Stream.of("a", "b").collect(Collectors.toList());
Arrays.asList 返回的是 固定大小、底層共享數組的 List 視圖,僅支持 get/set/contains 等只讀/替換操作。需增刪時,請顯式拷貝至 java.util.ArrayList。