Java泛型 extends,super和萬用字元的理解
阿新 • • 發佈:2019-01-24
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和&的型別引數中只能有一個類,其餘為介面。用法如下2.extends,super和?萬用字元一起使用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(); } } ///:~
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 Orange
true
*///:~