国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

Java ArrayList.toArray(T[]) 方法的參數類型是 T 而不是 E的原因分析

2019-11-26 14:27:04
字體:
來源:轉載
供稿:網友

前兩天給同事做 code review,感覺自己對 Java 的 Generics 掌握得不夠好,便拿出 《Effective Java》1 這本書再看看相關的章節。在 Item 24:Eliminate unchecked warnings 這一節中,作者拿 ArrayList 類中的 public <T> T[] toArray(T[] a) 方法作為例子來說明如何對變量使用 @SuppressWarnings annotation。

ArrayList 是一個 generic class,它是這樣聲明的:

Javapublic class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable

這個類的 toArray(T[] a) 方法是一個 generic method,它是這樣聲明和實現的:

@SuppressWarnings("unchecked")public <T> T[] toArray(T[] a) {if (a.length < size)// Make a new array of a's runtime type, but my contents:return (T[]) Arrays.copyOf(elementData, size, a.getClass());System.arraycopy(elementData, 0, a, 0, size);if (a.length > size)a[size] = null;return a;}

這個方法實際上是在 Collection 接口中聲明的。因為我們經常通過 ArrayList 使用它,這里就用 ArrayList 作為例子了。

1 為什么聲明為不同類型?

我的問題是:為什么這個方法使用類型 T,而不使用 ArrayList 的類型 E ? 也就是說,這個方法為什么不聲明成這樣:

Javapublic E[] toArray(E[] a);

如果類型相同的話,在編譯期間就可以發現參數的類型錯誤。如果類型不同,很容易產生運行時錯誤。比如下面這段代碼:

//創建一個類型為 String 的 ArrayListList<String> strList = new ArrayList<String>();strList.add("abc");strList.add("xyz");//將當前的 strList 轉換成一個 Number 數組。注意,下面的語句沒有任何編譯錯誤。Number[] numArray = strList.toArray(new Number[0]);

運行上面的代碼, Line 6 會拋出 java.lang.ArrayStoreException 異常。

如果 toArray 方法使用類型 E 的話,語句2就會產生編譯錯誤。編譯錯誤怎么說也比運行時錯誤親切啊。并且,generics 的主要目的就是為了類型安全,把類型轉換錯誤(ClassCastException)消滅在編譯期間。這個方法卻反其道而行之。難道這是一個大 bug? Java 的 bug 俺碰上過,但這個地方出 bug 我還是不太敢相信。

上網一查,這個問題早已被討論過多次了2, 3, 4。

2 可以提高靈活性

這樣的聲明更靈活,可以把當前 list 中的元素轉換成一個更一般類型的數組。比如,當前 list 的類型是 Integer,我們可以把它的元素轉換成一個 Number 數組。

List<Integer> intList = new ArrayList<Integer>();intList.add(1);intList.add(2);Number[] numArray = intList.toArray(new Number[0]);

如果這個方法聲明成類型 E,上面的代碼就會有編譯錯誤。 看起來,該方法聲明成下面這樣會更合適:

Javapublic <T super E> T[] toArray(T[] a);

不過, <T super E> 這樣的語法在 Java 中是不存在的。而且即使存在,對數組也不起作用。也正是因為這個原因,在使用這個方法時,即使 T 是 E 的父類,或 T 跟 E 相同,也不能完全避免 java.lang.ArrayStoreException 異常5, 6, 7 。請看下面兩段代碼。第一段代碼中 T 是 E 的父類,第二段代碼中 T 和 E 一樣。這兩段代碼都會拋出異常。

代碼一:

List<Integer> intList = new ArrayList<Integer>();intList.add(1);intList.add(2); Float[] floatArray = new Float[2];//Float 是 Number 的子類,所以 Float[] 是 Number[] 的子類Number[] numArray = floatArray;//下面的語句會拋出 ArrayStoreException 異常numArray = intList.toArray(numArray);

代碼二:

List<Number> intList = new ArrayList<Number>();//List 的類型是 Number。但 Number 是抽象類,只能存它的子類的實例intList.add(new Integer());intList.add(new Integer()); Float[] floatArray = new Float[];//Float 是 Number 的子類,所以 Float[] 是 Number[] 的子類Number[] numArray = floatArray;//下面的語句會拋出 ArrayStoreException 異常numArray = intList.toArray(numArray);

上面的異常都是由這個事實造成的:如果 A 是 B 的父類,那么 A[] 是 B[] 的父類。Java 中所有的類都繼承自 Object,Object[] 是所有數組的父類。

這個帖子8里舉了個例子,說明即使這個方法的類型聲明成 E 也不能避免 ArrayStoreException 異常。

該方法的文檔中也提到了這個異常:

ArrayStoreException if the runtime type of the specified array is not a supertype of the runtime type of every element in this list.

3 可以與 Java 1.5 之前的版本兼容

這個方法在 Java 引入 Generics 之前(JDK1.5 中引入了 Generics)就出現了9。那時它被聲明稱這樣:

Javapublic Object[] toArray(Object[] a)

Generics 出現后,許多類和方法就變成 generic 的了。這個方法也隨大流聲明成這樣:

Javapublic <T> T[] toArray(T[] a)

這樣聲明可以與 Java 1.5 之前的版本兼容10。

4 多

主站蜘蛛池模板: 宝坻区| 达拉特旗| 固镇县| 府谷县| 南漳县| 南涧| 双峰县| 汶川县| 东台市| 宜川县| 时尚| 敖汉旗| 湖口县| 泰州市| 饶阳县| 应用必备| 攀枝花市| 淳化县| 宁远县| 浮梁县| 广西| 罗源县| 白河县| 正蓝旗| 岳西县| 金川县| 冷水江市| 宾阳县| 盐边县| 温泉县| 咸阳市| 荣昌县| 当涂县| 刚察县| 广元市| 洮南市| 太仆寺旗| 抚远县| 惠安县| 康平县| 塔河县|