記錄一個異常:class java.util.ImmutableCollections$ListN cannot be cast to class java.util.ArrayList (java.util.ImmutableCollections$ListN and java.util.ArrayList
文章目錄
- 1、原因
- 2、解決方式一
- 3、解決方式二
- 4、關于不可變集合的補充
- 4.1 JDK8和9的對比
- 4.2 相關源碼
- 4.3 不可變集合的使用場景
1、原因
代碼中做了類型轉換,將一個不可變集合 (ImmutableCollections$ListN) 強制轉換為 可變集合 (ArrayList)
List<String> immutableList = List.of("A", "B", "C"); // Java 9+ 不可變列表
ArrayList<String> mutableList = (ArrayList<String>) immutableList; // ? 拋出 ClassCastException
業務相關錯誤代碼:
List<ClusterJob> jobs = result.get().getJobs().stream().sorted(Comparator.comparing(ClusterJob::getOrderUpdateTime).reversed()).toList();
ClusterJobResponse response = new ClusterJobResponse();
response.setJobs((ArrayList<ClusterJob>) jobs);
return BaseVo.createSuccessWithData(response);
而toList方法的源碼,是一個不可變集合:
default List<T> toList() {return (List<T>) Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray())));}
2、解決方式一
使用 new ArrayList<>(Collection) 復制
List<String> immutableList = List.of("A", "B", "C");
ArrayList<String> mutableList = new ArrayList<>(immutableList); // ? 復制數據
3、解決方式二
直接使用傳統的Collectors.toList()
List<ClusterJob> jobs = result.get().getJobs().stream().sorted(Comparator.comparing(ClusterJob::getOrderUpdateTime).reversed()).collect(Collectors.toList());
ClusterJobResponse response = new ClusterJobResponse();
response.setJobs((ArrayList<ClusterJob>) jobs);
4、關于不可變集合的補充
4.1 JDK8和9的對比
不可變集合(Immutable Collections)是指創建后內容不可修改的集合,任何嘗試修改(add、remove、set)的操作都會拋出 UnsupportedOperationException
- Java 8 及之前:使用 Collections.unmodifiableXXX() 做一個包裝(只是視圖,底層數據仍可能被修改)
- Java 9+:新增 List.of(), Set.of(), Map.of() 等工廠方法(真正的不可變集合)
public class Main {public static void main(String[] args) {List<String> original = new ArrayList<>();original.add("A");original.add("B");List<String> unmodifiable = Collections.unmodifiableList(original);// 成功original.add("C");// UnsupportedOperationExceptionunmodifiable.add("C");}
}
如上,使用Collections.unmodifiableList包裝后得到的集合,其不可被更新,但原始被包裝的集合,還是可以更改
Java9+以后,創建不可變集合:
List<String> immutableList = List.of("A", "B", "C");
Set<Integer> immutableSet = Set.of(1, 2, 3);
Map<String, Integer> immutableMap = Map.of("A", 1, "B", 2); // 空集合
List<String> emptyList = List.of();
Map<String, Integer> emptyMap = Map.of();
此時的集合,不可被更新,自然也不能被類型轉換,如 (ArrayList) List.of(...)
會拋異常,推薦使用這種方式,內存占用更小,性能更好
4.2 相關源碼
核心類:ImmutableCollections
throw UnsupportedOperationException在日常開發用的也很多,一個接口,不同的策略,有時候某一個實現類并不需要實現接口的某一個方法,此時可以直接拋這個異常
4.3 不可變集合的使用場景
用于定義一些存放固定值的常量或者成員變量:
public static final List<String> COUNTRIES = List.of("China", "US", "UK");
private static final Map<String, Integer> CACHE = Map.of("VIP", 100,"NORMAL", 10
);
用于防御性拷貝,List.copyOf創建不可變副本,此時,哪怕后面original被改變了,我自己還是不受影響
public void processData(List<String> original) {List<String> safeCopy = List.copyOf(original); // 創建不可變副本// 安全使用 safeCopy
}
當然,以下這個寫法也行
List<String> original = new ArrayList<>();
List<String> listNew = new ArrayList<>(original);
new ArrayList<>(original)
和 List.copyOf(original)
都會創建獨立副本,后續對 original 的修改不會影響副本,區別是:前者返回的一個可變集合,后者則是一個嚴格的不可變集合