1、何為泛型
首先泛型的本質便是類型參數化,通俗的說就是用一個變量來表示類型,這個類型可以是String,Integer等等不確定,表明可接受的類型,原理類似如下代碼
int pattern; //聲明一個變量未賦值,pattern可以看作是泛型
pattern = 4;
pattern = 5;//4和5就可以看作是String和Integer
泛型的具體形式見泛型類、泛型方法
*泛型方法舉例代碼如下
public <T> void show()
{
operation about T...
}
泛型參數類型聲明必須在返回類型之前
2、為何要引入泛型,即泛型與Object的優勢
由于泛型可以接受多個參數,而Object經過強制類型轉換可以轉換為任何類型,既然二者都具有相同的作用,為何還要引進泛型呢?
解答:泛型可以把使用Object的錯誤提前到編譯后,而不是運行后,提升安全性。以下用帶泛型的ArrayList和不帶泛型的Arraylist舉例說明
代碼1:
ArrayList al = new ArrayList();al.add("hello");
al.add(4);//自動裝箱
String s1 = (String)al.get(0);
String s2 = (String)al.get(1);//在編譯時沒問題,但在運行時出現問題
首先聲明無泛型的ArrayList時,其默認的原始類型是Object數組,既然為Object類型,就可以接受任意數據的賦值,因此編譯時沒有問題,但是在運行時,Integer強轉成String,肯定會出現ClassCastException.因此泛型的引入增強了安全性,把類轉換異常提前到了編譯時期。
3、類型擦除和原始類型
類型擦除的由來
在JAVA的虛擬機中并不存在泛型,泛型只是為了完善java體系,增加程序員編程的便捷性以及安全性而創建的一種機制,在JAVA虛擬機中對應泛型的都是確定的類型,在編寫泛型代碼后,java虛擬中會把這些泛型參數類型都擦除,用相應的確定類型來代替,代替的這一動作叫做類型擦除,而用于替代的類型稱為原始類型,在類型擦除過程中,一般使用第一個限定的類型來替換,若無限定則使用Object.
泛型方法的翻譯
因此在虛擬機翻譯泛型方法中,引入了橋方法,及在類型擦除后的show(Object t)中調用另一個方法,代碼如下:
public void show(Object t){
show((String) t);
}
4、泛型限定
泛型限定是通過?(通配符)來實現的,表示可以接受任意類型,那一定有人有疑問,那?和T(二者單獨使用時)有啥區別了,其實區別也不是很大,僅僅在對參數類型的使用上。例如:
? extends SomeClass 這種限定,說明的是只能接收SomeClass及其子類類型,所謂的“上限”
? super SomeClass 這種限定,說明只能接收SomeClass及其父類類型,所謂的“下限”
一下舉例? extends SomeClass說明一下這類限定的一種應用方式
由于泛型參數類型可以表示任意類型的類類型,若T要引用compareTo方法,如何保證在T類中定義了compareTo方法呢?利用如下代碼:
public <T extends Comparable> shwo(T a, T b){
int num = a.compareTo(b);
}
此處用于限定T類型繼承自Comparable,因為T類型可以調用compareTo方法。
可以有多個類型限定,例如:
<T extends Comparable & Serializable>
關于泛型類型限定的“繼承”誤區
總有些人誤把類型的限定當作繼承,比如:
//類型是這樣的
<Student extends Person>
//然后出現此類錯誤
ArrayList<Person> al = new ArrayList<Student>();
此處的<Person>, <Student>作為泛型的意思是ArrayList容器的接收類型,用一個簡單的例子來說明
ArrayList是一個大型養殖場,<Person>表明的是他能夠接收動物,而上邊的new語句生成的是一個只能夠接收豬的養殖場(ArrayList<Student>),即把一個大型養殖場建造成了一個養豬場,若是繼承的大型養殖場肯定是還能接受狗、羊....的,但是現在建造成了養豬場,那還能接受別的動物么?所以這肯定是錯誤的用法!簡而言之,泛型new時兩邊的類型參數必須一致。
5、泛型的一些基本規則約束
泛型的類型參數必須為類的引用,不能用基本類型(int, short, long, byte, float, double, char, boolean)
泛型是類型的參數化,在使用時可以用作不同類型(此處在說泛型類時會詳細說明)
泛型的類型參數可以有多個,代碼舉例如下:
public <T, E> void show(){ coding operation..... }
泛型可以使用extends, super, ?(通配符)來對類型參數進行限定
由于類型擦除,運行時類型查詢只適用于原始類型,比如instanceof、getClass()、強制類型轉換,a instanceof (Pair<Employe>),在類型擦除后便是 a instanceof Pair,因此以上運行的一些操作在虛擬機中操作都是對原始類型進行操作,無論寫的多么虛幻,都逃不出類型擦除,因為在虛擬機種并不存在泛型。
不能創建參數化類型的數組
不能實例化類型變量,及不能出現以下的類似代碼
T t = new T();//或T.Class
因為在類型擦除后,便是Object t = new Object();與用意不符合,即本意肯定不是要調用Object.
不能再靜態域或方法中出現參數類型
但是這樣的代碼就是正確的
class Test<T>{public static <T> T show() { action }}
因為此處的靜態方法是泛型方法,可以使用.
不能拋出或捕獲泛型類的實例
+不能拋出不能捕獲泛型類對象
+泛型類不能擴展Throwable,注意是類不能繼承Throwable,類型參數的限定還是可以的。
+catch子句不能使用類型變量,如下代碼:
此處的錯誤的原因不能存在同一個方法,在類型擦除后,Pair的方法為,public boolean equals(Object value),這與從Object.class中繼承下來的equals(Object obj)沖突。
一個類不能成為兩個接口類型的子類,而這兩個接口是同一接口的不同參數化。
例如:
class Calendar implements coparable<Calendar>{}class GregorianCalendar extends Calendar implements Comparable<GregorianCalendar>{} //error
當類型擦除后,Calendar實現的是Comparable,而GregorianCalendar繼承了Calendar,又去實現Comparable,必然出錯!
———————————————————————————————————————————————————————————————————————————————
先總結到此處。