何为泛型(Generics)
面向对象编程语言的多态特性包括:任一多态(ad hoc polymorphism,又名重载)、子类型多态(subtype polymorphism,又名覆盖)以及参数多态(parameter polymorphism,也即泛型)三种特性。泛型指的是程序语句编写时不考虑变量参数类型,或程序的设计与具体才用何种类型的变量无关,而在运行时决定参数类型的程序编写方式。
在较为高级成熟的编程语言中,泛型编程是程序语言自带的特性。比如在Python或Visual Basic中,声明变量时不需要声明变量的类型。Java 是从Java 5开始支持对类进行泛型编程的。
非泛型编程造成的问题
Java 5之前,当程序中需要设置与类型无关的变量时,只能采用Object变量。下面就是一个典型的Java 5之前的程序:
public class WeakPair {
private final Object first;
private final Object second;
public WeakPair(Object first, Object second){
this.first = first;
this.second = second;
}
public Object getFirst(){ return first;}
public Object getSecond(){ return second;}
public String toString(){
return "(" + first.toString() + "," + second.toString() + ")";
}
}
这样的程序有如下一些问题:
- 其调用程序的变量需要经过强制转换才能编译。
- 由于变量类型错误导致的程序问题不能在编译时被发现,而需要等到程序运行时才能发现。比如,如下的代码便会在运行时报TypeError异常:
WeakPair p2 = new WeakPair("Everything", 42);
int i2 = (int) p2.getFirst();
而采用泛型编程,调用程序的变量就不需要经过强制转化了,而且调用程序变量的类型错误能在编译时就被发现。比如,若将WeakPair改成泛型类之后,其调用示例如下:
如上图,程序中不符合泛型参数的调用在netbeans中就会被加上下划线。那么,这样的泛型类或泛型方法如何书写呢?
编写泛型类
Java中泛型的书写,即在类名后加上“<>”,然后再在“<>”中声明代表类型的参数即可。该参数一般采用一个大写字母,用E(entity)、T(type)、或K-V(用于类型中有键值对的情形),以下就是Pair对的泛型类书写方式。
public class Pair<T1, T2> {
private final T1 first;
private final T2 second;
/**
*
* @param first
* @param second
*/
public Pair(T1 first, T2 second){
this.first = first;
this.second = second;
}
public T1 getFirst(){ return first; }
public T2 getSecond(){ return second; }
public String toString(){
return "(" + first.toString() + "," + second.toString() + ")";
}
}
以下为它的一个调用方式:
Pair<Integer, String> p1 = new Pair<>(i, "Everything");
Pair<String, Integer> p2 = new Pair("Everything", 42);
//正确的调用
String s1 = p1.getSecond();
int i1 = p1.getFirst();
编写泛型函数
泛型还可以用于某一函数中,而类中的其他函数不是泛型的情况。以下是一个获取一个数组中所有元素的真子集的函数:
/*
* @author Dr.Thomas Christy, orginally created by:
* @author Peter Schachte <schachte@unimelb.edu.au>
* /
public static <T> T[][] combinations(T[] list) {
T[][] combos = (T[][])Array.newInstance(list.getClass(),
(int) Math.pow(2, list.length));
for (int i = 0 ; i < combos.length ; ++i) {
int count = 0;
for (int j = 0 ; j < list.length ; ++j) {
if ((i & 1<<j) != 0) ++count;
}
combos[i] = (T[])Array.newInstance(list.getClass().getComponentType(), count);
count = 0;
for (int j = 0 ; j < list.length ; ++j) {
if ((i & 1<<j) != 0) {
combos[i][count] = list[j];
++count;
}
}
}
return combos;
}
参考资料:
- W. Savitch, Absolute Java. Pearson Addison Wesley 6th Edition, 2013.
- T. Christy, Unimelb COMP90041 Programming and Software Development, 2018