一、泛型的定義
1墨辛、什么是java泛型?
泛型是Java SE 1.5的新特性趴俘,泛型的本質(zhì)是參數(shù)化類(lèi)型睹簇,也就是說(shuō)所操作的數(shù)據(jù)類(lèi)型被指定為一個(gè)參數(shù)。
這種參數(shù)類(lèi)型可以用在類(lèi)寥闪、接口和方法的創(chuàng)建中太惠,分別稱(chēng)為泛型類(lèi)、泛型接口疲憋、泛型方法凿渊。
2、為什么需要泛型?
Java語(yǔ)言引入泛型的好處是安全簡(jiǎn)單埃脏√侣啵可以將運(yùn)行時(shí)錯(cuò)誤提前到編譯時(shí)錯(cuò)誤。
在Java SE 1.5之前彩掐,沒(méi)有泛型的情況的下构舟,通過(guò)對(duì)類(lèi)型Object的引用來(lái)實(shí)現(xiàn)參數(shù)的“任意化”,“任意化”帶來(lái)的缺點(diǎn)是要做顯式的強(qiáng)制類(lèi)型轉(zhuǎn)換堵幽,
而這種轉(zhuǎn)換是要求開(kāi)發(fā)者對(duì)實(shí)際參數(shù)類(lèi)型可以預(yù)知的情況下進(jìn)行的狗超。對(duì)于強(qiáng)制類(lèi)型轉(zhuǎn)換錯(cuò)誤的情況,編譯器可能不提示錯(cuò)誤朴下,在運(yùn)行的時(shí)候才出現(xiàn)
異常努咐,這是一個(gè)安全隱患。泛型的好處是在編譯的時(shí)候檢查類(lèi)型安全殴胧,并且所有的強(qiáng)制轉(zhuǎn)換都是自動(dòng)和隱式的渗稍,提高代碼的重用率。
3溃肪、泛型的好處免胃?
(1)類(lèi)型安全。
通過(guò)知道使用泛型定義的變量的類(lèi)型限制惫撰,編譯器可以更有效地提高Java程序的類(lèi)型安全羔沙。
(2)消除強(qiáng)制類(lèi)型轉(zhuǎn)換。
消除源代碼中的許多強(qiáng)制類(lèi)型轉(zhuǎn)換厨钻。這使得代碼更加可讀扼雏,并且減少了出錯(cuò)機(jī)會(huì)。所有的強(qiáng)制轉(zhuǎn)換都是自動(dòng)和隱式的夯膀。
(3)提高性能诗充。
4、泛型使用的注意事項(xiàng)
(1)泛型類(lèi)型變量不能是基本數(shù)據(jù)類(lèi)型诱建。
就比如蝴蜓,沒(méi)有ArrayList<double>,只有ArrayList<Double>俺猿。因?yàn)楫?dāng)類(lèi)型擦除后茎匠,ArrayList的原始類(lèi)中的類(lèi)型變量(T)替換為Object,
但Object類(lèi)型不能存儲(chǔ)double值押袍。
(2)泛型的類(lèi)型參數(shù)可以有多個(gè)诵冒,用逗號(hào)分隔。
Node<T,E,V,K>
(3)不能對(duì)確切的泛型類(lèi)型使用instanceof操作谊惭。如下面的操作是非法的汽馋,編譯時(shí)會(huì)出錯(cuò)侮东。
if( arrayList instanceof ArrayList<String>){}//編譯錯(cuò)誤
(4)不能創(chuàng)建一個(gè)確切的泛型類(lèi)型的數(shù)組。
例如:
List<String>[] ls = new ArrayList<String>[10];//編譯報(bào)錯(cuò)
List<String>[] list = new ArrayList[10];//正確
List<?>[] ls = new ArrayList<?>[10];//正確
(5)泛型在靜態(tài)方法和靜態(tài)類(lèi)中的問(wèn)題
泛型類(lèi)中的靜態(tài)方法和靜態(tài)變量不可以使用泛型類(lèi)所聲明的泛型類(lèi)型參數(shù)
public /*static*/ class StaticGenerator<T> {
//泛型的靜態(tài)方法
public static <T> void show(T t){
System.out.println("泛型測(cè)試:t is " + t);
}
/*public static T one; //編譯錯(cuò)誤
public static T print(T one){ //編譯錯(cuò)誤
return null;
}*/
}
因?yàn)榉盒皖?lèi)中的泛型參數(shù)的實(shí)例化是在定義泛型類(lèi)型對(duì)象(例如ArrayList<Integer>)的時(shí)候指定的豹芯,而靜態(tài)變量和靜態(tài)方法不需要使用對(duì)象來(lái)調(diào)用悄雅。
對(duì)象都沒(méi)有創(chuàng)建,如何確定這個(gè)泛型參數(shù)是何種類(lèi)型告组,所以當(dāng)然是錯(cuò)誤的煤伟。但是要注意區(qū)分下面的一種情況:
泛型方法:在泛型方法中使用的T是自己在方法中定義的T,而不是泛型類(lèi)中的T木缝。
(6)泛型類(lèi)型引用傳遞問(wèn)題
在Java中便锨,像下面形式的引用傳遞是不允許的:
ArrayList<String> arrayList1=new ArrayList<Object>();//編譯錯(cuò)誤
ArrayList<Object> arrayList2=new ArrayList<String>();//編譯錯(cuò)誤
二、泛型的實(shí)現(xiàn)原理
1我碟、類(lèi)型擦除
ArrayList<String> arrayString = new ArrayList<String>();
ArrayList<Integer> arrayInteger = new ArrayList<Integer>();
System.out.println(arrayString.getClass() == arrayInteger.getClass());//輸出:true
在編譯期間放案,所有的泛型信息都會(huì)被擦除,List<Integer>和List<String>類(lèi)型矫俺,在編譯后都會(huì)變成List類(lèi)型(原始類(lèi)型)吱殉。
Java中的泛型基本上都是在編譯器這個(gè)層次來(lái)實(shí)現(xiàn)的,這也是Java的泛型被稱(chēng)為“偽泛型”的原因厘托。
2友雳、原始類(lèi)型
原始類(lèi)型就是泛型類(lèi)型擦除了泛型信息后,在字節(jié)碼中真正的類(lèi)型铅匹。無(wú)論何時(shí)定義一個(gè)泛型類(lèi)型押赊,相應(yīng)的原始類(lèi)型都會(huì)被自動(dòng)提供。
原始類(lèi)型的名字就是刪去類(lèi)型參數(shù)后的泛型類(lèi)型的類(lèi)名包斑。擦除類(lèi)型變量流礁,并替換為限定類(lèi)型(T為無(wú)限定的類(lèi)型變量,用Object替換)罗丰。
//泛型類(lèi)型
class Pair<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
//原始類(lèi)型
class Pair {
private Object value;
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
在Pair<T>中神帅,T是一個(gè)無(wú)限定的類(lèi)型變量,所以用Object替換萌抵。如果是Pair<T extends Number>找御,擦除后,類(lèi)型變量用Number類(lèi)型替換绍填。
3萎坷、橋接方法
Node<T>:聲明Node<String>還是Node<Integer>,到了運(yùn)行期間沐兰,JVM統(tǒng)統(tǒng)視為Node<Object>。
原因:對(duì)于泛型代碼蔽挠,Java編譯器實(shí)際上還會(huì)偷偷幫我們實(shí)現(xiàn)一個(gè)Bridge method(橋接方法)住闯。
解決辦法:
Node<T extends Comparable<T>> 代替 Node<T>
public class Node<T> {
public T data;
public Node(T data) { this.data = data; }
public void setData(T data) {
System.out.println("Node.setData");
this.data = data;
}
}
public class MyNode extends Node<Integer> {
public MyNode(Integer data) { super(data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}
測(cè)試代碼如下:
MyNode mn = new MyNode(5);
Node n = mn; // A raw type - compiler throws an unchecked warning
n.setData("Hello"); // Causes a ClassCastException to be thrown.
Integer x = mn.data;
System.out.println(n.toString());
System.out.println(x);
編譯之后的代碼如下:
class MyNode extends Node {
// Bridge method generated by the compiler
public void setData(Object data) {
setData((Integer) data);
}
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
// ...
}