Java泛型类

泛型概念

  先来看看泛型类的示例:

1
2
3
4
5
6
7
8
9
10
11
public class Pair<T>
{
private T first;
private T second;
public Pair() { first = null; second = null; }
public Pair(T first, T second) { this.first = first; this.second = second; }
public T getFirst() { return first; }
public T getSecond() { return second; }
public void setFirst(T newValue) { first = newValue; }
public void setSecond(T newValue) { second = newValue; }
}

  当然,定义泛型类可以引入多个类型变量,例如:

1
2
3
public class Pair<T, U> {
....
}

  泛型类可以看作普通类的工厂,还可以在普通类中定义一个泛型方法:

1
2
3
4
5
6
7
class ArrayAlg
{
public static <T> T getMiddle(T... a)
{
return a[a.length / 2];
}
}

  这里类型变量放在修饰符public static后面,实际调用时:

1
String middle = ArrayAlg.<String>getMiddle("John", "Q.", "Public");

  大多数情况下你可以省略String,直接

1
String middle = ArrayAlg.getMiddle("John", "Q.", "Public");

  但前提是参数都是这些类的超类唯一,编译器才不会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Interval<T extends Comparable & Serializable> implements Serializable {
private T lower;
private T upper;
//. . .
public Interval(T first, T second) {
if (first.compareTo(second) <= 0) {
lower = first;
upper = second;
} else {
lower = second;
upper = first;
}
}
}

  我们知道在jvm层面上没有泛型的概念,类型变量都会被原始类型所替代,无限定的就用Object,如果有多个限定类型,就用第一个替换,例如上例中会用Comparable替换。

泛型约束和限制

  1. 泛型不能用于基本类型

  2. 运行时类型查询只适用于原始类型,例如

    1
    2
    3
    Pair<A> a = new Pair<A>();
    Pair<B> b = new Pair<B>();
    a.getClass() == b.getClass(); // 相等,都是Pair.class
  3. 不能抛出也不能捕获泛型类实力

  4. 不能声明泛型参数化的数组

    1
    ArrayList<String>[] arr = new ArrayList<String>[10];
  5. 不能实例化泛型实例变量,如Pair的构造器这样就是非法的:

    1
    2
    3
    4
    public Pair() {
    first = new T(); // ERROR
    second = new T();
    }

泛型继承

  如果A是B的子类,那么Pair<A>不是Pair<B>的子类。

数组情况则不同,A是B的子类,可以将A[]赋值给B[]

  但是将参数化类型转换成原始类型会导致类型错误么?很遗憾,编译器只是会给个warning,可以编译:

1
2
3
Pair<Manager> managerBuddies = new Pair<>(ceo, cfo);
Pair rawBuddies = managerBuddies; // OK
rawBuddies.setFirst(new File(". . .")); // only a compile-time warning

  这看起来比较恐怖,但是当使用getFirst()调用转换成Manager时,会抛出ClassCastException

通配符

  之前代码中有Pair<? extends Employee>表示类型参数是Employee的子类

1
2
3
Pair<Manager> managerBuddies = new Pair<>(ceo, cfo);
Pair<? extends Employee> wildcardBuddies = managerBuddies; // OK
wildcardBuddies.setFirst(lowlyEmployee); // compile-time error

  泛型类翻译以后:

1
2
? extends Employee getFirst()
void setFirst(? extends Employee)

  setFirst方法只知道需要Employee的子类型,但是确切不知道是哪种类型,因此连编译都不能通过。除了extends关键字还存在super关键字表示父类,例如Pair<? super Manger>,那么经过泛型翻译以后,Pair<? super Manager>有以下方法:

1
2
? super Manager getFirst()
void setFirst(? super Manager)

  这时setFirst方法可以调用Manager对象以及Manager的子类型了,再看下面一个典型的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void minmaxBonus(Manager[] a, Pair<? super Manager> result)
{
if (a == null || a.length == 0) return;
Manager min = a[0];
Manager max = a[0];
for (int i = 1; i < a.length; i++)
{
if (min.getBonus() > a[i].getBonus()) min = a[i];
if (max.getBonus() < a[i].getBonus()) max = a[i];
}
result.setFirst(min);
result.setSecond(max);
}

  因此直观地将,带有super限定的通配符可以向泛型对象写入,带有extends类型限定的通配符可以从泛型对象读取。

无限定通配符

  无限定通配符是指Pair<?>,这看起来跟原始的Pair类型一样,类型Pair<?>有如下方法:

1
2
? getFirst()
void setFirst(?)

  这里getFirst方法返回值只能赋给一个Object,setFirst方法不能被调用