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

          http://www.aygfsteel.com/sean/archive/2005/08/09/9630.html

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

          其實(shí),sean的文中也提到,Java對(duì)泛型的支持其實(shí)就是在編譯器中做了做手腳,增加了一些強(qiáng)制類型轉(zhuǎn)換的代碼,也就是說(shuō)原來(lái)需要我們手動(dòng)寫(xiě)的一些強(qiáng)制類型轉(zhuǎn)換的代碼,在泛型的世界里,Java編譯器就幫我們做了。

          下面來(lái)一步步的分析泛型數(shù)組的問(wèn)題:

          Java中的泛型做了什么

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

              
          public T getValue() {
                  
          return value;
              }

              
          public void setValue(T t) {
                  value 
          = t;
              }
          }

          使用javap命令反編譯生成的GenTest類的class文件,可以得到下面的輸出:
          javap --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."<init>":()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當(dāng)作Object來(lái)處理的。如果我們規(guī)定泛型是Numeric類或者其子類,那么在這里泛型T就是被當(dāng)作Numeric類來(lái)處理的。

          好,既然GenTest類中沒(méi)有什么乾坤,那么我們繼續(xù)看使用GenTest的時(shí)候又什么新東西:
          public class UseGenTest {

              
          public static void main(String[] args) {
                  String value 
          = "value";
                  GenTest
          <String> test = new GenTest<String>();
                  test.setValue(value);
                  String nv 
          = test.getValue();
              }
          }

          使用javap命令反編譯生成的GenTest類的class文件,可以得到下面的輸出:
          D:\mymise\eclipse\workspace\Test\bin>javap --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."<init>":()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."<init>":()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

          }

          重點(diǎn)在17、20和23三處。17就是調(diào)用getValue方法。而20則是關(guān)鍵——類型檢查。也就是說(shuō),在調(diào)用getValue方法之后,并沒(méi)有直接把返回值賦值給nv,而是先檢查了返回值是否是String類型,換句話說(shuō),“String nv = test.getValue();”被編譯器變成了“String nv = (String)test.getValue();”。最后,如果檢查無(wú)誤,在23處才會(huì)賦值。也就是說(shuō),如果沒(méi)有完成類型檢查,則會(huì)報(bào)出類似ClassCastException,而代碼將不會(huì)繼續(xù)向下執(zhí)行,這就有效的避免了錯(cuò)誤的出現(xiàn)。
          也就是說(shuō):在類的內(nèi)部,泛型類型就是被基類型代替的(默認(rèn)是Object類型),而對(duì)外,所有返回值類型為泛型類型的方法,在真正使用返回值之前,都是會(huì)經(jīng)過(guò)類型轉(zhuǎn)換的。

          為什么不支持泛型的數(shù)組?

          根據(jù)上面的分析可以看出來(lái),泛型其實(shí)是挺嚴(yán)謹(jǐn)?shù)模f(shuō)白了就是在“編譯的時(shí)候通過(guò)增加強(qiáng)制類型轉(zhuǎn)換的代碼,來(lái)避免用戶編寫(xiě)出可能引發(fā)ClassCastException的代碼”。這其實(shí)也算是Java引入泛型的一個(gè)目的。

          但是,一個(gè)頗具諷刺意味的問(wèn)題出現(xiàn)了:如果允許了泛型數(shù)組,那么編譯器添加的強(qiáng)制類型轉(zhuǎn)換的代碼就會(huì)有可能是錯(cuò)誤的。
          看下面的例子:
          //下面的代碼使用了泛型的數(shù)組,是無(wú)法通過(guò)編譯的
          GenTest<String> genArr[] = new GenTest<String>[2];
          Object[] test 
          = genArr;
          GenTest<StringBuffer> strBuf = new GenTest<StringBuffer>();
          strBuf.setValue(new StringBuffer());
          test[0= strBuf;
          GenTest
          <String> ref = genArr[0]; //上面兩行相當(dāng)于使用數(shù)組移花接木,讓Java編譯器把GenTest<StringBuffer>當(dāng)作了GenTest<String>
          String value = ref.getValue();// 這里是重點(diǎn)!

          上面的代碼中,最后一行是重點(diǎn)。根據(jù)本文第一部分的介紹,“String value = ref.getValue()”會(huì)被替換成“String value = (String)ref.getValue()”。當(dāng)然我們知道,ref實(shí)際上是指向一個(gè)存儲(chǔ)著StringBuffer對(duì)象的GenTest對(duì)象。所以,編譯器生成出來(lái)的代碼是隱含著錯(cuò)誤的,在運(yùn)的時(shí)候就會(huì)拋出ClassCastException。

          但是,如果沒(méi)有“String value = ref.getValue();”這行代碼,那么程序可以說(shuō)沒(méi)有任何錯(cuò)誤。這全都是Java中多態(tài)的功勞。我們來(lái)分析一下,對(duì)于上面代碼中創(chuàng)建出來(lái)的GenTest對(duì)象,其實(shí)無(wú)論value引用實(shí)際指向的是什么對(duì)象,對(duì)于類中的代碼來(lái)說(shuō)都是沒(méi)有任何影響的——因?yàn)樵贕enTest類中,這個(gè)對(duì)象僅僅會(huì)被當(dāng)作是基類型的對(duì)象(在這里也就是Object的對(duì)象)來(lái)使用。所以,無(wú)論是String的對(duì)象,還是StringBuffer的對(duì)象,都不可能引發(fā)任何問(wèn)題。舉例來(lái)說(shuō),如果調(diào)用valued的hashcode方法,那么,如果value指向的是String的對(duì)象,實(shí)際執(zhí)行的就是String類中的hashcode方法,如果是StringBuffer的對(duì)象,那么實(shí)際執(zhí)行的就是StringBuffer類中的hashcode方法。

          從這里可以看出,即使支持泛型數(shù)組也不會(huì)帶來(lái)什么災(zāi)難性的后果,最多就是可能引發(fā)ClassCastException。而且平心而論,這個(gè)還是程序員自己的錯(cuò)誤,實(shí)在算不得是Java編譯器的錯(cuò)誤。

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

          當(dāng)然制定JSR的那幫子人可能沒(méi)學(xué)過(guò)中文,但是他們肯定是發(fā)現(xiàn)了這個(gè)令他們糾結(jié)的問(wèn)題。被標(biāo)榜為Java 5重要feature的泛型竟然陷入了這么一個(gè)怪圈。于是,他們?cè)谀硞€(gè)月黑風(fēng)高的晚上,在某個(gè)猥瑣的會(huì)議室內(nèi),悄悄的決定一不做二不休——不支持泛型的數(shù)組了。(本段內(nèi)容系作者猜測(cè),并無(wú)任何事實(shí)根據(jù),如有雷同,純粹巧合。)

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 嘉禾县| 普安县| 北票市| 溧水县| 腾冲县| 翁牛特旗| 花垣县| 宝兴县| 盐亭县| 阳朔县| 桂平市| 张家界市| 安陆市| 建宁县| 柘荣县| 无极县| 永寿县| 荣成市| 旌德县| 客服| 兖州市| 漳浦县| 荔波县| 马鞍山市| 贵定县| 塔河县| 盈江县| 大厂| 东至县| 平舆县| 额敏县| 太谷县| 青田县| 枝江市| 湖南省| 威远县| 武宣县| 辉县市| 遂川县| 黄山市| 东丰县|