Java Generic (1)
1 泛型类
??? 泛型允许对类型进行抽象,最常见的泛型类是容器类。例如:
List<String> list = new ArrayList<String>();list.add("1");list.add("2");for(String s: list) {System.out.println(s);}??? 以上例子中,如果试图向list中添加一个Integer对象,那么会导致编译错误。编译器会进行类型检查,这避免了使用非泛型容器类时常见的强制类型转换。泛型类是具有一个或者多个类型变量(type variable)的类。以下是个简单的例子:
import java.util.ArrayList;import java.util.Iterator;import java.util.List;public class Bag<E> {private List<E> bag = new ArrayList<E>();public Iterator<E> iterator() {return this.bag.iterator();}public boolean add(E e) {return this.bag.add(e);}public boolean remove(Object o) {return this.bag.remove(o);}public boolean contains(Object e) {return this.bag.contains(e);}public boolean addAll(Bag<? extends E> b) {return this.bag.addAll(b.bag);}public boolean removeAll(Bag<?> b) {return this.bag.removeAll(b.bag);}public boolean containsAll(Bag<?> b) {return this.bag.containsAll(b.bag);} public void clear() {this.bag.clear();} List<E> getBag() {return this.bag;}}import java.util.Collections;import java.util.Comparator;public class Bags {public static <E> void sort(Bag<E> b, Comparator<? super E> c) {Collections.sort(b.getBag(), c);}public static <T extends Comparable<? super T>> T max(Bag<T> b) {return Collections.max(b.getBag());}public static <E> void copy(Bag<E> dest, Bag<? extends E> src) {dest.addAll(src);}}??? 需要注意的是,不能抛出或者捕获泛型类的实例,泛型类继承自Throwable也是不合法的。此外也不能声明泛型类实例的数组,例如以下代码会造成编译错误:
Bag<Integer> b3[] = new Bag<Integer>[10]; // Cannot create a generic array of Bag<Integer>
?
2 泛型方法
??? 泛型方法是指带有类型参数的方法,类型变量的位置是修饰符和返回类型之间。在调用泛型方法的时候,具体类型的位置是方法名前的尖括号中,在绝大多数的情况下,由于编译器可以判断出具体类型,因此也可以省略具体类型。以下是个简单的例子:
public class Base { public String getName() { return "Base"; } public static <T> void print(T t) { System.out.println(t.toString()); } public static void main(String args[]) { Base base = new Base(); Base.<Base>print(base); Base.print(base); }}??? 以上例子中的print方法内只能调用T从Object类继承的方法。如果希望将T限定为Base及其子类,那么可以使用extends设定上限,或者使用super设定下限(C++中不能限定参数变量的类型),例如:
public static <T extends Base> void print(T t) {System.out.println(t.getName());}??? 类型变量也可以有多个限定,限定类型用&分割。如果限定类型可以包含多个接口,但是至多有一个类(单继承),如果有一个类,那么它必须是第一个限定类型。例如:?
T extends Comparable & Serializable
?
3 擦除
??? 编译器会为每个泛型类型自动提供一个原始类型(raw type)。其类型变量会被擦除(因此Java泛型没有C++模板类的代码膨胀问题),并用其限定类型代替,如果没有限定类型,那么使用Object。不能使用原始数据类型作为类型变量,例如没有Bag<int>,但是可以有Bag<Integer>。 由于类型变量会被擦除,因此不管泛型类的类型变量是什么,它的所有实例的运行时类是相同的,例如以下代码的输出如下:
Bag<Integer> b1 = new Bag<Integer>();Bag<String> b2 = new Bag<String>();System.out.println(b1.getClass());System.out.println(b2.getClass());System.out.println(b1.getClass() == b2.getClass());
??? class Bag
??? class Bag
??? true
??? 由于类的静态成员变量和成员方法也被类的所有实例共享,因此不能在类的静态成员变量和成员方法中引用类型变量。例如以下的代码会导致编译错误:
public class Bag<E> {private static E INSTANCE; // Cannot make a static reference to the non-static type Epublic static E getInstance() { // Cannot make a static reference to the non-static type Ereturn INSTANCE;}}??? 不能通过类型变量构造实例,但是可以通过Class.newInstance和Array.newInstance来构造实例。例如以下的代码会造成编译错误:
public Bag() {E e1 = new E(); // Cannot instantiate the type EE e2[] = new E[10]; }??? 在擦除后,也不能引起冲突。考虑如下代码:
public class Bag<E> {public boolean equals(E e) { // Name clash: The method equals(E) of type Bag<E> has the same erasure as equals(Object) of type Object but does not override itreturn true;}}??? 以上的Bag类的类型变量被擦除后的代码如下:
public class Bag<E> {public boolean equals(Object e) {return true;}}??? 其equals方法与Object.equals(Object obj)方法冲突。