Java不允許創(chuàng)建泛型數(shù)組的原因
上面鏈接中的Xuan Luo
的回答很好. 簡單來說就是Java數(shù)組必須有一個具體的類型信息, 但是因為泛型擦除, 所以無法提供具體的類型信息給數(shù)組, 因而無法創(chuàng)建泛型數(shù)組. 而容器其實泛型擦除后根本沒類型信息了, 所以全部的List<AnyType>
最后都是List
. 看下面這個例子:
public static void testTypeInformation(){
String[] strings1 = new String[0];
String[] strings2 = new String[0];
Integer[] integers = new Integer[0];
System.out.println(String.format("strings1.class == strings2.class: %s", strings1.getClass() == strings2.getClass()));
// 直接這樣寫編譯器直接給出了錯誤
// strings1.getClass() == integers.getClass()
// System.out.println(String.format("strings1.class == integers.class: %s", strings1.getClass() == integers.getClass()));
Class<?> stringArrayClass = strings1.getClass();
Class<?> integerArrayClass = integers.getClass();
System.out.println(String.format("strings1.class == integers.class: %s", stringArrayClass == integerArrayClass));
// 再看容器類
List<String> stringList = new ArrayList<>();
List<Integer> integerList = new ArrayList<>();
System.out.println(String.format("stringList.class == integerList.class: %s", stringList.getClass() == integerList.getClass()));
}
輸出:
strings1.class == strings2.class: true
strings1.class == integers.class: false
stringList.class == integerList.class: true
可以看到數(shù)組
是確確實實有具體類型, 而泛型容器最終都是容器class本身, 沒有新的類生成.
一些創(chuàng)建泛型數(shù)組的方法
調(diào)用反射包的方法
/**
* 該方法可以安全地創(chuàng)建泛型數(shù)組
* 調(diào)用反射包里的Array.newInstance(type, length)方法.
* 具體的實現(xiàn)是靠native方法.
* @param type 元素類型
* @param length 長度
* @param <T> 類型
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T[] createArray(Class<T> type, int length){
return (T[]) Array.newInstance(type, length);
}
強制類型轉(zhuǎn)換 (錯誤方法)
/**
* 編譯不會報錯, 但是運行時
* 只要T不是Object, 那一定會報錯
* 因為Java對于數(shù)組的定義, 是必須要有具體類型的
* 雖然 String 可以轉(zhuǎn)換為 Object, 但是Object不一定能轉(zhuǎn)換成String
* 一樣的道理 String[] 可以轉(zhuǎn)換為 Object[], 但是Object[]不一定能
* 轉(zhuǎn)換成String[], 另外String[]轉(zhuǎn)換為Object[]是合法的, 但是千萬
* 不要這樣做, 很可能會出錯, 因為轉(zhuǎn)換為Object[]就能存任意引用了,
* 但是底層仍然是String[], 如果存的不是String, 那么就會報錯.
* @param len 長度
* @param <T> 類型參數(shù), 可由類型推導推導得知, 也可以顯式
* 指定, ClassName.<Type>function()
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T[] createArray(int len){
return (T[]) new Object[len];
}
測試:
// ClassCastException
// Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
// 使用了類型推導
try {
Integer[] integers = createArray(10);
} catch (ClassCastException e){
System.out.println("exception: " + e.getMessage());
}
// 也可以這樣, 顯式指定類型
// Integer[] integerArray = GenericArray.<Integer>createArray(10);
因為數(shù)組的實際類型是Object[]
, 是無法轉(zhuǎn)型為Integer[]
的.
限制訪問的Object[]方法
/**
* 使用底層Object[]來存儲元素的折中做法
* 不能暴露Object[]出去, 否則無法保證
* 往里面加的類型是否安全
* @param <T>
*/
public static class MyArray<T>{
// 存放實際的數(shù)據(jù)
// 因為所有對象都能向上轉(zhuǎn)型為Object
// 所有用Object[]來存各類對象是沒問題的
private Object[] innerArray;
public MyArray(int len){
innerArray = new Object[len];
}
@SuppressWarnings("unchecked")
public T get(int index){
// 只要set方法中傳入的確實是T類型的對象
// 那么這里一定是安全的
return (T) innerArray[index];
}
public void set(int index, T item){
innerArray[index] = item;
}
@Override
public String toString() {
return Arrays.toString(innerArray);
}
}
測試:
MyArray<String> stringArray = new MyArray<>(3);
stringArray.set(0, "The");
stringArray.set(1, "safe");
stringArray.set(2, "generic array");
System.out.println(stringArray);
輸出:
[The, safe, generic array]