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

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

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

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

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

          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當作Object來處理的。如果我們規定泛型是Numeric類或者其子類,那么在這里泛型T就是被當作Numeric類來處理的。

          好,既然GenTest類中沒有什么乾坤,那么我們繼續看使用GenTest的時候又什么新東西:
          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

          }

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

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

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

          但是,一個頗具諷刺意味的問題出現了:如果允許了泛型數組,那么編譯器添加的強制類型轉換的代碼就會有可能是錯誤的。
          看下面的例子:
          //下面的代碼使用了泛型的數組,是無法通過編譯的
          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]; //上面兩行相當于使用數組移花接木,讓Java編譯器把GenTest<StringBuffer>當作了GenTest<String>
          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的泛型竟然陷入了這么一個怪圈。于是,他們在某個月黑風高的晚上,在某個猥瑣的會議室內,悄悄的決定一不做二不休——不支持泛型的數組了。(本段內容系作者猜測,并無任何事實根據,如有雷同,純粹巧合。)

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 新绛县| 高密市| 德兴市| 措美县| 曲阜市| 乡城县| 东乌珠穆沁旗| 张家港市| 措勤县| 遵化市| 宜阳县| 宁明县| 龙胜| 临猗县| 盘锦市| 宝应县| 乌海市| 包头市| 基隆市| 吉隆县| 抚州市| 达拉特旗| 曲阜市| 介休市| 大埔区| 衡山县| 乌拉特前旗| 梁河县| 辽阳市| 雷山县| 武乡县| 神农架林区| 青浦区| 宁乡县| 固原市| 庆安县| 湘潭市| 文昌市| 钟山县| 个旧市| 久治县|