Java Tutorials-04-泛型

泛型类和泛型方法

泛型类

public class Pair<T> {
private T first;
private T second;

public T getFirst() {...}
public T getSecond() {...}
public setFirst(T) {...}
public setSecond(T) {...}
}

泛型方法

public Class ArrayAlg {
public static <T> T getMiddle (T t) {
return t[t.length / 2];
}
}

// 调用泛型方法
ArrayAlg.<String>getMiddle("Hello");
// 或者不用指定泛型方法的T, 编译期自行推断也是可以的:
ArrayAlg.getMiddle("Hello");

泛型的类型限定

public static <T extends Comparable> T min(T[] a) {
T min = a[0];
for(int i=0; i < a.length; i++) {
if(a[i] < min) min = a[i];
}
return min;
}

如果T需要多个类型限定: <T extends Comparable & Serializable>

类型擦除

  • JVM没有”泛型类”这种类型, java代码被编译后生成的字节代码, 这个过程中所有的泛型类型要被替换, 原则:
    • 有类型限定的, 替换为第一个限定类型, T extends Comparable & Serializable被替换为Comparable
    • 无类型限定, 替换为Object, T被替换为Object

对类型查询的影响

1. instanceof

Pair<String> s = new Pair<String>(); // 擦除后为 Pair<Object> s
if(s instanceof Pair<T>) { // Pair<T>擦除后为Pair<Object>
// yes
}
if(s instanceof Pair<Double>) {
// yes
}

2. getClass()

Pair<String> s = new Pair<String>();
Pair<Double> d = new Pair<Double>();
if(s.getClass() == d.getCLass()) { // getClass()总是返回Pair<Object>
// yes
}
// 打印 s.getClass().toString() , 得到的类型是 ‘Pair’

不能创建泛型类数组

// 试图创建泛型类型的数组会在编译期报错:
Pair<String>[] arr = new Pair<String>[1]; // error !
Pair<String>[] arr = Array.newInstance(Pair<String>.getClass(),1); // error !

原因是数组一旦创建会记住元素的类型, 当试图向数组中存储不同的类型时会报错, Pair<String>[]这样声明的泛型数组, 擦除后变为Pair<Object>[].

不能实例化泛型

不能使用像new T(), new T[N], T.class这样的表达式.

通配符<?>

无限定通配符

public static boolean Foo(List<?> list) {
return list.get(0) != null;
}

List<?>表示持有某种特定类型的List,但是不知道具体是哪种类型。那么我们可以向其中添加对象吗?当然不可以,因为并不知道实际是哪种类型,所以不能添加任何类型,这是不安全的。

上界通配符

? extends ClassType表示ClassType的任何子类

先看一段代码:

List<? extends Fruit> list = new ArrayList<Apple>();

// Compile Error: can’t add any type of object:
// flist.add(new Apple());
// flist.add(new Fruit());
// flist.add(new Object());

// 只能向list里添加null
list.add(null);

// get是可以编译通过的
list.get(0);

做了泛型的向上转型 (List<? extends Fruit> flist = new ArrayList<Apple>()),那么我们也就失去了向这个List添加任何对象的能力,即使是Object也不行。
那么上界通配符有什么用呢?

public class GenericTest {
public static void func(List<? extends Fruit> list) {
for(i=0; i<list.size(); i++) {
Fruit fruit = list.get(i);
System.out.println(fruit.getName());
}

list.add(new Apple()); // Compile Error!
list.add(new Object()); // Compile Error!
}
}

List<? extends Fruit> list 表示一个List, 里面存储的类型是Fruit的派生类, 从list里get出来的类型至少是Fruit, 或者Fruit的派生类, 可以调用Fruit类的方法.
传递给GenericTest.func()的参数可以是List<Apple>, 也可以是List<Lemon>,
上界通配符<? extends Base>, 可以调用基类Base里定义的方法, 也可以get, 但是不可以set

下界通配符

? super Integer表示Integer的超类, 只能用于setter.

void setFirst(Pair<? super Integer>);

限定符和泛型的一些问题…

泛型中无界通配符<?><T>的区别?

  • <T>用在类或方法的定义里: public class ArrayList<T>
  • <?>通配符用在”调用”的地方, 通配符是拿来使用定义好的泛型的, 可以使用?的一般满足:
    1. 方法定义里只使用Object的方法,跟?类型无关;
    2. 使用中不依赖于泛型, 最典型的是Class<?> ...
  • 无限定通配符表示匹配任意类。ArrayList<?>ArrayList<Object> 看上去有点类似,但实际却不一样。
    1. ArrayList<?>是任意 ArrayList<T> 的超类;
    2. List<Apple>List<? extends Fruit>的子类(假设Apple继承自Fruit)
    3. ArrayList<Object> 并不是ArrayList<T> 的超类;