java 不支持泛型_分析一下為什么JAVA不支持泛型類型的數組

首先大家參考一下這篇文章

sean的這篇文章大部分是對的,但是到最后的結論部分“想想看,我們本來定義的是裝Map的數組,結果我們卻可以往里面放任何Map,接下來如果有代碼試圖按原有的定義去取值,后果是什么不言自明。”,我覺得可以討論討論。

其實,sean的文中也提到,Java對泛型的支持其實就是在編譯器中做了做手腳,增加了一些強制類型轉換的代碼,也就是說原來需要我們手動寫的一些強制類型轉換的代碼,在泛型的世界里,Java編譯器就幫我們做了。

下面來一步步的分析泛型數組的問題:

Java中的泛型做了什么

首先看一下Java中的泛型做了什么。看下面這段代碼:public class GenTest {

T value;

public T getValue() {

return value;

}

public void setValue(T t) {

value = t;

}

}

使用javap命令反編譯生成的GenTest類的class文件,可以得到下面的輸出:

javap -c -p GenTest

Compiled from "GenTest.java"

public class GenTest extends java.lang.Object{

java.lang.Object value;

public GenTest();

Code:

0: aload_0

1: invokespecial #12; //Method java/lang/Object."":()V

4: return

public java.lang.Object getValue();

Code:

0: aload_0

1: getfield #23; //Field value:Ljava/lang/Object;

4: areturn

public void setValue(java.lang.Object);

Code:

0: aload_0

1: aload_1

2: putfield #23; //Field value:Ljava/lang/Object;

5: return

}

我們清楚的看到,泛型T在GenTest類中就是Object類型(java.lang.Object value;)。同樣,get方法和set方法也都是將泛型T當作Object來處理的。如果我們規定泛型是Numeric類或者其子類,那么在這里泛型T就是被當作Numeric類來處理的。

好,既然GenTest類中沒有什么乾坤,那么我們繼續看使用GenTest的時候又什么新東西:public class UseGenTest {

public static void main(String[] args) {

String value = "value";

GenTest test = new GenTest();

test.setValue(value);

String nv = test.getValue();

}

}

使用javap命令反編譯生成的GenTest類的class文件,可以得到下面的輸出:D:\mymise\eclipse\workspace\Test\bin>javap -c -p UseGenTest

Compiled from "UseGenTest.java"public class UseGenTest extends java.lang.Object{public UseGenTest();

Code:

0: aload_0

1: invokespecial #8; //Method java/lang/Object."":()V

4: return

public static void main(java.lang.String[]);

Code:

0: ldc #16; //String value

2: astore_1

3: new #18; //class GenTest

6: dup

7: invokespecial #20; //Method GenTest."":()V

10: astore_2

11: aload_2

12: aload_1

13: invokevirtual #21; //Method GenTest.setValue:(Ljava/lang/Object;)V

16: aload_2

17: invokevirtual #25; //Method GenTest.getValue:()Ljava/lang/Object;

20: checkcast #29; //class java/lang/String

23: astore_3

24: return

}

重點在17、20和23三處。17就是調用getValue方法。而20則是關鍵——類型檢查。也就是說,在調用getValue方法之后,並沒有直接把返回值賦值給nv,而是先檢查了返回值是否是String類型,換句話說,“String nv = test.getValue();”被編譯器變成了“String nv =(String)test.getValue();”。最后,如果檢查無誤,在23處才會賦值。也就是說,如果沒有完成類型檢查,則會報出類似ClassCastException,而代碼將不會繼續向下執行,這就有效的避免了錯誤的出現。

也就是說:在類的內部,泛型類型就是被基類型代替的(默認是Object類型),而對外,所有返回值類型為泛型類型的方法,在真正使用返回值之前,都是會經過類型轉換的。

為什么不支持泛型的數組?

根據上面的分析可以看出來,泛型其實是挺嚴謹的,說白了就是在“編譯的時候通過增加強制類型轉換的代碼,來避免用戶編寫出可能引發ClassCastException的代碼”。這其實也算是Java引入泛型的一個目的。

但是,一個頗具諷刺意味的問題出現了:如果允許了泛型數組,那么編譯器添加的強制類型轉換的代碼就會有可能是錯誤的。

看下面的例子://下面的代碼使用了泛型的數組,是無法通過編譯的

GenTest genArr[] = new GenTest[2];

Object[] test = genArr;

GenTest strBuf = new GenTest();

strBuf.setValue(new StringBuffer());

test[0] = strBuf;

GenTest ref = genArr[0]; //上面兩行相當於使用數組移花接木,讓Java編譯器把GenTest當作了GenTest

String value = ref.getValue();// 這里是重點!

上面的代碼中,最后一行是重點。根據本文第一部分的介紹,“String value = ref.getValue()”會被替換成“String value =(String)ref.getValue()”。當然我們知道,ref實際上是指向一個存儲着StringBuffer對象的GenTest對象。所以,編譯器生成出來的代碼是隱含着錯誤的,在運的時候就會拋出ClassCastException。

但是,如果沒有“String value = ref.getValue();”這行代碼,那么程序可以說沒有任何錯誤。這全都是Java中多態的功勞。我們來分析一下,對於上面代碼中創建出來的GenTest對象,其實無論value引用實際指向的是什么對象,對於類中的代碼來說都是沒有任何影響的——因為在GenTest類中,這個對象僅僅會被當作是基類型的對象(在這里也就是Object的對象)來使用。所以,無論是String的對象,還是StringBuffer的對象,都不可能引發任何問題。舉例來說,如果調用valued的hashcode方法,那么,如果value指向的是String的對象,實際執行的就是String類中的hashcode方法,如果是StringBuffer的對象,那么實際執行的就是StringBuffer類中的hashcode方法。

從這里可以看出,即使支持泛型數組也不會帶來什么災難性的后果,最多就是可能引發ClassCastException。而且平心而論,這個還是程序員自己的錯誤,實在算不得是Java編譯器的錯誤。

但是從另一個角度看,這確實是個巨大的諷刺:泛型是為了消滅ClassCastException而出現的,但是在這個時候它自己卻引發了ClassCastException。咱們中國人把這個叫做搬起石頭砸自己的腳。

當然制定JSR的那幫子人可能沒學過中文,但是他們肯定是發現了這個令他們糾結的問題。被標榜為Java 5重要feature的泛型竟然陷入了這么一個怪圈。於是,他們在某個月黑風高的晚上,在某個猥瑣的會議室內,悄悄的決定一不做二不休——不支持泛型的數組了。(本段內容系作者猜測,並無任何事實根據,如有雷同,純粹巧合。)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值