[改善Java代碼]Java的泛型是類型擦除的


泛型可以減少強制類型的轉換,可規范集合的元素類型,還可以提高代碼的安全性和可讀性,正是因為有了這些優點,自從Java引入泛型之后,項目的編碼規則上便多了一條,優先使用泛型.

Java泛型(Generic)的引入加強了參數類型的安全性,減少了類型的轉換,它與C++中的模板templates比較類似.但是有一點,Java的反省在編譯期有效,在運行期被刪除,也就是說所有的泛型參數類型在編譯后都會被清除掉.

看如下代碼:

import java.util.List;

public class Foo {
    //arrayMethod接受數組參數,並進行重載
    public void arrayMethod(String[] strArray){}
    public void arrayMethod(Integer[] intArray){}
    //listMethod接收泛型List參數,並進行重載
    public void listMethod(List<String> strList){}
    public void listMethod(List<Integer> intList){}
}

編寫了4個方法,arrayMethod方法接收String數組和Integer數組,這是一個典型的重載,listMethod接收元素類型為String和Integer的List變量.

但是這段代碼不能通過編譯,不能通過通過編譯是在listMethod方法上.

提示:Erasure of method listMethod(List<String>) is the same as another method in type Foo  &&  Erasure of method listMethod(List<Integer>) is the same as another method in type Foo

是說listMethod對應的這兩個方法在編譯時擦除類型后的方式是listMethod(List<E>),它與另外一個方法重復,通俗的說就是方法簽名重復,這就是Java泛型擦除引起問題:在編譯后所有的泛型類型都會做相應的轉化.

轉換規則如下:

1.List<String>,List<Integer>,List<T>泛型擦除后的類型為List.

2.List<String>[]擦除之后的類型是List[]

3.List<? extends E>,List<? super E>擦除之后對應類型是List<E>

4.List<T extends Serializable & Cloneable>擦除之后為List<Serializable>

Java編譯后的字節碼中已經沒有泛型的任何信息了,也就說一個泛型類和一個普通類在經過編譯后都指向了同一字節碼,比如Foo<T>類,經過編譯后將只有一份Foo.class類.

不管是Foo<String>還是Foo<Integer>引用的都是同一個字節碼,Java之所以這樣處理有兩方面原因:

1.避免JVM大換血.C++的泛型生命期延續到了運行期,而Java是在編譯器擦除掉的,如果JVM也把泛型類型延續到運行期,那么JVM就需要進行大量的重構工作了.

2.版本兼容,在 編譯期擦除可以更好的支持原生類型RawType,在Java1.5或1.6平台上,即使聲明一個List這樣的原生類型也是可以正常編譯通過的,只是會產生警告信息而已.

這樣就可以解釋如下問題了:

(1)泛型的class對象都是相同的.

每個類都有一個class屬性,泛型化不會改變class屬性的返回值.例如:

 1 import java.util.ArrayList;
 2 import java.util.List;
 3 
 4 public class Client {
 5     public static void main(String[] args) {
 6         List<String> ls = new ArrayList<String>();
 7         List<Integer> li = new ArrayList<Integer>();
 8         System.out.println(li.getClass() == li.getClass());
 9     }
10 }

 

 以上代碼會返回true,List<String>和List<Integer>擦除后的類型都是List,沒有任何的區別.

(2)泛型數組初始化時不能聲明泛型的類型

原因很簡單,可以聲明一個帶有泛型參數的數組,但是不能初始化該數組,因為執行了類型擦除操作,List<Object>[] 與List<String>就是一回事了,編譯器拒絕如此的聲明.

(3)instanceof不允許穿在泛型參數

原因 一樣,泛型類型的被擦除了.

 1 import java.util.ArrayList;
 2 import java.util.List;
 3 
 4 public class Client {
 5     public static void main(String[] args) {
 6         //List<String>[] listArray = new List<String>[];
 7         /*
 8         如上語句報錯
 9         Multiple markers at this line
10         - Variable must provide either dimension expressions or an array initializer
11         - Cannot create a generic array of List<String>        
12          */
13         List<String>[] listArray2; //只聲明不初始化沒有問題.
14         //======================================分割線============
15         
16         List<String> list = new ArrayList<String>();
17         System.out.println(list instanceof List);//沒有泛型就不會報錯,
18         //System.out.println(list instanceof List<String>);//有泛型就報錯.
19         /*
20         Cannot perform instanceof check against parameterized type List<String>.
21         Use the form List<?> instead since further generic type information will be erased at runtime 
22          */
23         
24         List<String> list2 = new ArrayList<String>();
25         List l = list2;
26         l.add(123);
27     }
28 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM