最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2016年04月07日 11:50:52   作者:小米干飯  
這篇文章主要介紹了Java ArrayList.toArray(T[]) 方法的參數(shù)類型是 T 而不是 E的原因分析的相關(guān)資料,需要的朋友可以參考下

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

ArrayList 是一個(gè) generic class,它是這樣聲明的:

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

這個(gè)類的 toArray(T[] a) 方法是一個(gè) generic method,它是這樣聲明和實(shí)現(xiàn)的:

@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;
}

這個(gè)方法實(shí)際上是在 Collection 接口中聲明的。因?yàn)槲覀兘?jīng)常通過 ArrayList 使用它,這里就用 ArrayList 作為例子了。

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

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

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

如果類型相同的話,在編譯期間就可以發(fā)現(xiàn)參數(shù)的類型錯(cuò)誤。如果類型不同,很容易產(chǎn)生運(yùn)行時(shí)錯(cuò)誤。比如下面這段代碼:

//創(chuàng)建一個(gè)類型為 String 的 ArrayList
List<String> strList = new ArrayList<String>();
strList.add("abc");
strList.add("xyz");
//將當(dāng)前的 strList 轉(zhuǎn)換成一個(gè) Number 數(shù)組。注意,下面的語句沒有任何編譯錯(cuò)誤。
Number[] numArray = strList.toArray(new Number[0]);

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

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

上網(wǎng)一查,這個(gè)問題早已被討論過多次了2, 3, 4。

2 可以提高靈活性

這樣的聲明更靈活,可以把當(dāng)前 list 中的元素轉(zhuǎn)換成一個(gè)更一般類型的數(shù)組。比如,當(dāng)前 list 的類型是 Integer,我們可以把它的元素轉(zhuǎn)換成一個(gè) Number 數(shù)組。

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

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

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

不過, <T super E> 這樣的語法在 Java 中是不存在的。而且即使存在,對數(shù)組也不起作用。也正是因?yàn)檫@個(gè)原因,在使用這個(gè)方法時(shí),即使 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 是抽象類,只能存它的子類的實(shí)例
intList.add(new Integer());
intList.add(new Integer()); 
Float[] floatArray = new Float[];
//Float 是 Number 的子類,所以 Float[] 是 Number[] 的子類
Number[] numArray = floatArray;
//下面的語句會拋出 ArrayStoreException 異常
numArray = intList.toArray(numArray);

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

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

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

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 之前的版本兼容

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

Javapublic Object[] toArray(Object[] a)

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

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

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

4 多啰嗦兩句

這個(gè)方法需要一個(gè)數(shù)組參數(shù)。如果這個(gè)數(shù)組的 length 大于或等于當(dāng)前 list 的 size,list 中的元素就會存儲到這個(gè)數(shù)組當(dāng)中;如果這個(gè)數(shù)組的 length 小于當(dāng)前 list 的 size,就會創(chuàng)建一個(gè)新的數(shù)組,并把當(dāng)前 list 中的元素存入到這個(gè)新創(chuàng)建的數(shù)組中。為提高效率,如果可能,傳入的數(shù)組的 length 要大于或等于 list 的 size,以避免該方法新建數(shù)組。

List<Integer> intList = new ArrayList<Integer>();
intList.add();
intList.add();
//傳入一個(gè)數(shù)組,它的長度為 
Number[] numArray = intList.toArray(new Number[]); //語句
//傳入一個(gè)數(shù)組,它的長度與 intList 的長度相等
Number[] numArray = intList.toArray(new Number[intList.size()]); //語句

另外,作為參數(shù)的數(shù)組不能為 null ,否則的話會拋出 NullPointerException 異常。

Footnotes:

1
Effective Java (2nd Edition)
2
Link
3
Link
4
Link
5
Link
6
Link
7
Link
8
Link
9
Link
10
Link
Created: 2016-04-06 Wed 21:14
Emacs 24.5.1 (Org mode 8.2.10)
Validate

以上內(nèi)容是小編給大家介紹的Java ArrayList.toArray(T[]) 方法的參數(shù)類型是 T 而不是 E的原因分析,希望對大家有所幫助!

相關(guān)文章

  • 代碼詳解Java猴子選王問題(約瑟夫環(huán))

    代碼詳解Java猴子選王問題(約瑟夫環(huán))

    本篇文章通過實(shí)例給大家分析了java約瑟夫環(huán)這個(gè)經(jīng)典內(nèi)容,有興趣的跟著小編一起學(xué)習(xí)下吧。
    2018-02-02
  • springboot websocket簡單入門示例

    springboot websocket簡單入門示例

    這篇文章主要介紹了springboot websocket簡單入門示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-08-08
  • java?-jar啟動服務(wù)并輸出日志常用命令小結(jié)

    java?-jar啟動服務(wù)并輸出日志常用命令小結(jié)

    這篇文章主要介紹了在Linux環(huán)境下運(yùn)行JAR包的幾種方法,包括在命令結(jié)尾添加&使其在后臺運(yùn)行,使用nohup使程序不掛斷運(yùn)行,以及將日志輸出到指定文件或丟棄,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-03-03
  • Java設(shè)計(jì)模式之23種設(shè)計(jì)模式詳解

    Java設(shè)計(jì)模式之23種設(shè)計(jì)模式詳解

    這篇文章主要介紹了Java設(shè)計(jì)模式之23種設(shè)計(jì)模式詳解,設(shè)計(jì)模式使代碼編制真正工程化,設(shè)計(jì)模式是軟件工程的基石,項(xiàng)目中合理的運(yùn)用設(shè)計(jì)模式可以完美的解決很多問題,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • SpringBoot 并發(fā)登錄人數(shù)控制的實(shí)現(xiàn)方法

    SpringBoot 并發(fā)登錄人數(shù)控制的實(shí)現(xiàn)方法

    這篇文章主要介紹了SpringBoot 并發(fā)登錄人數(shù)控制的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • Java初學(xué)之繼承與多態(tài)

    Java初學(xué)之繼承與多態(tài)

    這篇文章主要介紹了Java 繼承與多態(tài)的深入理解的相關(guān)資料,子類繼承父類的特征和行為,使得子類具有父類的各種屬性和方法?;蜃宇悘母割惱^承方法,使得子類具有父類相同的行為,需要的朋友可以參考下
    2021-07-07
  • java中找不到符號的解決方案

    java中找不到符號的解決方案

    這篇文章主要介紹了java中找不到符號的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • java虛擬機(jī)學(xué)習(xí)筆記基礎(chǔ)篇

    java虛擬機(jī)學(xué)習(xí)筆記基礎(chǔ)篇

    在本篇文章里小編給大家整理的是關(guān)于java虛擬機(jī)學(xué)習(xí)筆記的相關(guān)內(nèi)容,分享了一些基礎(chǔ)知識點(diǎn),需要的朋友們參考下。
    2019-06-06
  • SpringBoot結(jié)合Neo4j自定義cypherSql的方法

    SpringBoot結(jié)合Neo4j自定義cypherSql的方法

    這篇文章主要介紹了SpringBoot結(jié)合Neo4j自定義cypherSql,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-11-11
  • java JTree JCheckBox樹復(fù)選框詳解

    java JTree JCheckBox樹復(fù)選框詳解

    這篇文章主要為大家詳細(xì)介紹了java JTree JCheckBox樹復(fù)選框的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11

最新評論

皋兰县| 沁源县| 武鸣县| 色达县| 土默特左旗| 什邡市| 涡阳县| 吉水县| 桂阳县| 五华县| 治县。| 南雄市| 资兴市| 合山市| 微博| 扶余县| 桐乡市| 阿拉善左旗| 安陆市| 上高县| 陈巴尔虎旗| 抚顺市| 龙胜| 山西省| 怀远县| 湾仔区| 长顺县| 莱芜市| 建昌县| 电白县| 离岛区| 东阳市| 遂宁市| 五寨县| 阳山县| 天台县| 连南| 滨州市| 阳朔县| 马尔康县| 泉州市|