泛型類
一個泛型類 (generic class) 就是具有一個或多個類型變量的類。
- 泛型類可以有多個類型變量。 例如 , 可以定義 Pair 類 , 其中第一個域和第二個域使用不同的類型,類定義中的類型變量指定方法的返回類型以及域和局部變量的類型。
public class a<T,U>{}
泛型方法
當(dāng)某個類只有一個方法需要使用泛型時,可不必將整個類聲明為泛型類,可以單獨(dú)聲明泛型方法。
- 泛型方法可以定義在普通類中, 也可以定義在泛型類中。
- 當(dāng)調(diào)用一個泛型方法時,在方法名前的尖括號中放人具體的類型。
public class ArrayAlg{
public static <T> T getMiddle(T... a){
return a[a.length / 2];
}
}
String middle = ArrayAlg.<String>getMiddle("a","b","c");
//等同于,編譯器有足夠的信息能夠推斷出所調(diào)用的方法
String middle = ArrayAlg.getMiddle("a","b","c")
類型變量的限定
限定表示對變量進(jìn)行進(jìn)一步限制,必須繼承自某類或者實(shí)現(xiàn)某接口。
- <T extends BoundingType〉示 T 應(yīng)該是綁定類型的子類型 (subtype) 。T 和綁定類型可以是類, 也可以是接口 。 選擇關(guān)鍵字 extends 的原因是更接近子類的概念。
- 一個類型變量或通配符可以有多個限定,限定類型用 “ & ” 分隔, 而逗號用來分隔類型變量,有個限定多個接口,但同時只能限定一個類,如果包含類時,需要將類作為第一位。
public static <A extends Comparable,B extends Comparable> void test(A a,B b){}
類型擦除
無論何時定義一個泛型類型,都自動提供了一個相應(yīng)的原始類型(raw type) 。原始類型的名字就是刪去類型參數(shù)后的泛型類型名。 擦除類型變M , 并替換為限定類型(無限定的變量用Object)。
public class A<T>{
private T a;
private T b;
}
//由于T是“無限定變量”擦除后將轉(zhuǎn)變成原始類型(Object)
public class B{
private Object a;
private Object b;
}
- 原始類型用第一個限定的類型變量來替換 , 如果沒有給定限定就用 Object 替換。
public class A<T extends Comparable & Serializable> implements Serializable{
private T a;
private T b;
}
//將轉(zhuǎn)化為
public class A implements Serializable{
private Comparable a;
private Comparable b;
}
- 當(dāng)程序調(diào)用泛型方法時, 如果擦除返回類型 ,編譯器插入強(qiáng)制類型轉(zhuǎn)換。
Pair<Employee> buddies = ...;
Employee buddy = buddies.getFirst();
//編譯器把這個方法調(diào)用翻譯為兩條虛擬機(jī)指令
//對原始方法 Pair.getFirst()的調(diào)用,此時泛型方法將返回Object
//將返回的Object類型強(qiáng)制轉(zhuǎn)換為Employee類型。
虛擬機(jī)對當(dāng)get或set一個泛型字段時也會插人強(qiáng)制類型轉(zhuǎn)換。
-
橋方法
在繼承泛型類型的時候,橋方法的合成是為了避免類型變量擦除所帶來的多態(tài)災(zāi)難。
public class a<T>{
private T name;
public void setName(T name){}
public T getName(){}
}
//類型擦除后轉(zhuǎn)變?yōu)?public class a{
private Object name;
public void setName(Object name){}
public Object getName(){}
}
public class b extends a<String>{
public void setName(String name){}
public String getName(){}
}
//類型擦除后轉(zhuǎn)變?yōu)?重載了setName方法而不是重寫
public class b extends a{
public void setName(String name){}
//編譯器會生成一個橋方法來實(shí)現(xiàn)多態(tài)
public void setName(Object name){
setName(String(name));
}
//編譯器會生成一個橋方法實(shí)現(xiàn)對getName的覆蓋
public Object getName(){
return String(this.name);
}
}
-
虛擬機(jī)中沒有泛型 , 只有普通的類和方法 。
所有的類型參數(shù)都用它們的限定類型替換 。
橋方法被合成來保持多態(tài) 。
為保持類型安全性 , 必要時插人強(qiáng)制類型轉(zhuǎn)換