首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 強制類型轉換陷阱:List.toArray() 導致 ClassCastException 解析

     編輯:Java 相關知識

問題現場

List list = new ArrayList<>();
list.add("Hello");
list.add("World");

// 錯誤寫法:編譯通過,運行拋 ClassCastException
String[] arr = (String[]) list.toArray(); 

// 正確寫法
String[] arr2 = list.toArray(new String[0]); // 或 new String[list.size()]

根因深度解析

1. List.toArray() 簽名與實現

// List 接口
Object[] toArray();                    // 無參版本
 T[] toArray(T[] a);                // 有參版本 (泛型)

// ArrayList 實現 (簡化)
public Object[] toArray() {
    return Arrays.copyOf(elementData, size); // 返回 Object[]!
}

public  T[] toArray(T[] a) {
    if (a.length < size)
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size) a[size] = null;
    return a;
}

2. 類型擦除與運行時類型

  • Java 泛型擦除後,List 運行時只是 List,元素類型為 Object。
  • toArray() 無參版本只能返回 Object[],因為編譯器不知道具體元素類型。
  • 數組卻保留具體類型:String[] 運行時類型就是 String[],Object[] 無法強轉為 String[] (雖然 String 是 Object 子類,但數組協變導致 String[] 是 Object[] 子類,反向轉換需檢查)。

3. 為什麼 (String[]) list.toArray() 會拋異常?

Object[] objArr = list.toArray();  // 實際類型 Object[]
String[] strArr = (String[]) objArr; // 執行時類型檢查失敗:Object[] cannot be cast to String[]

JVM 運行時檢測到 objArr 真實類型是 Object[],而非 String[],拋出 ClassCastException。

正確姿勢對比

list.toArray(new String[0])list.toArray(new String[list.size()])(String[]) list.toArray()list.toArray(new Object[0])
寫法結果性能推薦度
✅ 正確JVM 逃逸分析優化,無額外分配⭐⭐⭐⭐⭐ (Java 8+ 推薦)
✅ 正確精確分配,避免擴容拷貝⭐⭐⭐⭐ (大列表微優)
❌ ClassCastException--🚫 禁用
✅ 返回 Object[]--⚠️ 需後續手動轉換

原理:有參版本如何保證類型安全?

// 關鍵:a.getClass() 獲取運行時數組組件類型
(T[]) Arrays.copyOf(elementData, size, a.getClass());

傳入 new String[0] 時,a.getClass() 返回 String[].class,Arrays.copyOf 以此創建 正確類型的新數組,最後安全返回 T[] (即 String[])。

擴展:Map 集合轉數組

Map map = new HashMap<>();
map.put("a", 1);

// Key 陣列
String[] keys = map.keySet().toArray(new String[0]);

// Value 陣列 (注意:值類型需一致)
Integer[] values = map.values().toArray(new Integer[0]);

// Entry 陣列
Map.Entry[] entries = map.entrySet().toArray(new Map.Entry[0]);

最佳實踐總結

  1. 永遠使用有參版本:list.toArray(new T[0]) (Java 6+) 或 list.toArray(new T[list.size()])。
  2. 避免無參 toArray():僅當你確定只需 Object[] 時使用。
  3. 泛型擦除認知:集合運行時不保留泛型類型,數組保留——這是核心矛盾點。
  4. 代碼審查重點:攔截所有 (SomeType[]) collection.toArray() 模式。

總結

List.toArray() 無參版本因泛型擦除只能返回 Object[],強轉為具體類型數組必失敗。正解是調用有參版本 toArray(new String[0]),利用傳入數組的運行時類型資訊創建正確類型數組。這是 Java 集合轉數組的「鐵律」,請寫入團隊編碼規約並配置靜態檢查規則 (SpotBugs/Error Prone) 自動攔截。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved