1. 程式人生 > >JAVA泛型容器的型別檢查

JAVA泛型容器的型別檢查

泛型容器是通過指定容器包含物件的型別,由編譯器保證物件型別的正確性,在編譯階段就能檢查出型別錯誤。如下列將List<Long>物件longList賦予一個List<GenericTest>物件gtList,會報編譯錯誤。

publicclassGenericTest   

{

publicstaticList<Long>longList= Arrays.asList (1L ,2L);

publicstaticvoidmain(String args[]){

//下面這條語句編譯會報錯

       List<GenericTest> gtList=

longList ; 

   }            

}

如果這只是在編譯檢查,那麼我們是否可以繞過編譯檢查呢?我們先將List<Long>物件longList賦予一個raw List變數rawList,然後再將這個rawList強制型別轉換成List<GenericTest>型別。結果編譯通過了,並且執行時也沒有報錯。我們成功地騙過了編譯器。

publicclassGenericTest

{

publicstaticList<Long>longList= Arrays.asList (1L ,2L);

publicstaticvoidmain(String args[]){

        List rawList=longList;

        //這個是可以編譯通過,成功的騙過了編譯器

       List<GenericTest> gtList= rawList; 

    }

}

接著,如果我們使用List<GenericTest>型別的變數gtList,那會發生什麼事。注意gtList引用的容器裡面放的實際上是Long型別物件。這會發生什麼事,會引用一段錯誤的記憶體嗎?結果我們發現String result = gtList.get(0).stringValue;語句正確地在執行時丟擲 java.lang.ClassCastException

異常。分析 main函式的位元組碼可知,在該語句之前插入了型別轉換檢查位元組碼,導致了這個異常的丟擲。看來Java設計者已經考慮到這一點,在泛型容器物件使用前加上了型別檢查,防止這樣的情況。

publicclassGenericTest

{

publicstaticList<Long>longList= Arrays.asList (1L ,2L);

public StringstringValue ="ss";

publicstaticvoidmain(String args[]){

        List rawList=longList;

        //這個是可以編譯通過,成功的騙過了編譯器

       List<GenericTest> gtList= rawList; 

        //會在執行時丟擲java.lang.ClassCastException

        String result =gtList.get (0).stringValue;

    }

}

位元組碼分析如下:

0 getstatic #28<variable/GenericTest.longList> //獲得類變數longList, 並放入棧頂

3 astore_1  //將棧頂引用放入第一個本地變數 rawList

4 aload_1 //將第一個本地變數rawList,放入棧頂

5 astore_2 //將棧頂引用放入第二個本地變數tgList,這裡我們可以看到將rawList賦值給tgList,沒有任何型別檢測,所以執行通過

6 aload_2

7 iconst_0

8 invokeinterface #43<java/util/List.get> count 2

13 checkcast #1 <variable/GenericTest> // 檢測型別轉換,當我們使用容器中物件時,會有型別檢測,導致丟擲java.lang.ClassCastException

16 getfield #37<variable/GenericTest.stringValue> // 獲得例項屬性,並放入棧頂

19 astore_3  //將棧頂引用放入第三個本地變數 result

20 return