1.java泛型的特點 通常情況下,一個編譯器處理泛型有兩種方式:
1.Code specialization。在執行個體化一個泛型類或泛型方法時都產生一份新的目標代碼(位元組碼or二進位代碼)。例如,針對一個泛型list,可能需要 針對string,integer,float產生三份目標代碼。
2.Code sharing。對每個泛型類只產生唯一的一份目標代碼;該泛型類的所有執行個體都映射到這份目標代碼上,在需要的時候執行類型檢查和類型轉換。
C++中的模板(template)是典型的Code specialization實現。C++編譯器會為每一個泛型類執行個體產生一份執行代碼。執行代碼中integer list和string list是兩種不同的類型。這樣會導致代碼膨脹(code bloat),不過有經驗的C++程式員可以有技巧的避免代碼膨脹。
Code specialization另外一個弊端是在參考型別系統中,浪費空間,因為參考型別集合中元素本質上都是一個指標。沒必要為每個類型都產生一份執行代碼。而這也是Java編譯器中採用Code sharing方式處理泛型的主要原因。
Java編譯器通過Code sharing方式為每個泛型型別建立唯一的位元組碼錶示,並且將該泛型型別的執行個體都映射到這個唯一的位元組碼錶示上。將多種泛型類形執行個體映射到唯一的位元組碼錶示是通過類型擦除(type erasue)實現的。
2.什麼是類型擦除。
類型擦除指的是通過型別參數合并,將泛型型別執行個體關聯到同一份位元組碼上。編譯器只為泛型型別產生一份位元組碼,並將其執行個體關聯到這份位元組碼上。類型擦除的關鍵在於從泛型型別中清除型別參數的相關資訊,並且再必要的時候添加類型檢查和類型轉換的方法。
類型擦除可以簡單的理解為將泛型java代碼轉換為普通java代碼,只不過編譯器更直接點,將泛型java代碼直接轉換成普通java位元組碼。
類型擦除的主要過程如下:
1.將所有的泛型參數用其最左邊界(最頂級的父類型)類型替換。
2.移除所有的型別參數。 3.extends,super和萬用字元的介紹 1.extends extends不僅可以用來表示繼承一個父類,可以用在泛型類的定義上。表示參數類型繼承自某個類或者實現了某個介面。exte nds和&結合使用可以表示參數類型必須要繼承和實現的類型和介面。注意,extends和&的型別參數中只能有一個類,其餘為介面。用法如下
package generics;class O<T>{T item;}class HoldItem<T> { T item; HoldItem(T item) { this.item = item; } T getItem() { return item; }}class Colored2<T extends HasColor> extends HoldItem<T> { Colored2(T item) { super(item); } java.awt.Color color() { return item.getColor(); }}class ColoredDimension2<T extends Dimension & HasColor>extends Colored2<T> { ColoredDimension2(T item) { super(item); } int getX() { return item.x; } int getY() { return item.y; } int getZ() { return item.z; }}class Solid2<T extends Dimension & HasColor & Weight>extends ColoredDimension2<T> { Solid2(T item) { super(item); } int weight() { return item.weight(); }}public class InheritBounds { public static void main(String[] args) { Solid2<Bounded> solid2 = new Solid2<Bounded>(new Bounded()); solid2.color(); solid2.getY(); solid2.weight(); }} ///:~
2.extends,super和。萬用字元一起使用
import java.util.*;class Fruit {}class Apple extends Fruit {}class Jonathan extends Apple {}class Orange extends Fruit {}public class GenericsAndCovariance { public static void main(String[] args) { // Wildcards allow covariance: List<? extends Fruit> flist = new ArrayList<Apple>(); flist=new ArrayList<Orange>(); //編譯失敗,不能添加任何類型 //flist.add(new Fruit()); //flist.add(new Object()); //flist.add(new Object()); // We know that it returns at least Fruit: Fruit f = flist.get(0); List<? super Fruit> list = new ArrayList<Fruit>(); list.add(new Apple());//可以 list.add(new Fruit());//可以 list.add(new Jonathan()); }} ///:~</span> List<?extends Fruit> flist=new ArrayList<Apple>()表明flist引用的list是Fruit的子類,具體是Apple還是Orange並不知道,所以在使用list.add的類型實際是。extends Fruit,所以不能使用add添加任何對象,包括Object和Fruit對象,因為如果可以,則可通過flist.add((Fruit)new Orange())將Orange對象添加到Apple的List中。get實際上返回的是Object對象然後轉型為Fruit,所以可以使用。 super關鍵字表示所引用的List的是Fruit或者Fruit的父類。所以可以將Oragnge,Apple都添加到list中,因為它們都是Fruit的子類型。再次驗證如下
package generics;public class Holder<T> { private T value; public Holder() {} public Holder(T val) { value = val; } public void set(T val) { value = val; } public T get() { return value; } public boolean equals(Object obj) { return value.equals(obj); } public static void main(String[] args) { Holder<Apple> Apple = new Holder<Apple>(new Apple()); Apple d = Apple.get(); Apple.set(d); // Holder<Fruit> Fruit = Apple; // Cannot upcast Holder<? extends Fruit> fruit = Apple; // OK Fruit p = fruit.get(); d = (Apple)fruit.get(); // Returns 'Object' try { Orange c = (Orange)fruit.get(); // No warning } catch(Exception e) { System.out.println(e); } // fruit.set(new Apple()); // Cannot call set() // fruit.set(new Fruit()); // Cannot call set() System.out.println(fruit.equals(d)); // OK }} /* Output: (Sample)java.lang.ClassCastException: Apple cannot be cast to Orangetrue*///:~