在程序中通常只对固定类型的对象进行操作。有些代码可以对多种不同类型的对象进行操作。实际使用的类型在代码中只是以参数形式出现的占位符。在具体实例化时,用实际类型替代其中的类型占位符,这种方式被称为泛型编程(generic programming)。泛型适用于对抽象类型进行处理,可以有效地减少代码重复,通过一份代码即可对不同类型的对象进行操作。java语言发展到J2SE 5.0才引入了泛型的特性。泛型的主要动机是为了实现类型安全的集合类。除此之外,泛型还可以用来创建处理抽象类型的新类型。
为了实现类型安全的集合类,J2SE 5.0引入了泛型的语言特性。泛型中包含的具体内容比较多,主要包括泛型类和泛型方法的声明和实例化。泛型的引入也对Java标准库中的很多API造成了影响。泛型类型与一般类型的区别在于,泛型类型有形式类型参数(type parameter)。
见下例,与一般的类型不同,泛型类型声明中的“<T>”用来表示形式类型参数T,形式类型参数T可以用在泛型类型实现的代码中:
public class ObjectHolder<T> {
private T obj;
public T getObject() {
return obj;
}
public void setObject(T obj) {
this.obj = obj;
}
}
泛型类的使用与一般的Java类并没有太大区别,只是需要为其中声明的形式类型参数指定实际的类型。如下例,形式类型参数T在实例化泛型类型时被替换成实际类型String:
ObjectHolder<String> holder = new ObjectHolder<String>();
holder.setObject("Hello");
String str = holder.getObject();
在创建出泛型类的对象之后,该对象在使用时的类型是受限的,比如上例中的holder对象,在调用setObject方法时,参数的类型只能是String类型;getObject方法的返回值也是String类型。
如果在一个类型中使用了形式类型参数,则称该类型为泛型类型(generic type)。上例中的ObjectHolder类就是一个泛型类。泛型类型可以被实例化,在实例化之后,泛型类型声明中的形式类型参数被替换成实际的类型。实例化之后的泛型类型被称为参数化类型(parameterized type)。上例中,ObjectHolder<String>是一种参数化类型。对于同一个泛型类型来说,可能的参数化类型的数量非常多。根据使用的实际类型,叁数化类型分为两类:一类是不带通配符的类型,另外一类是带通配 符的类型。通配符(wildcard)“?”的作用是表示一组类型的集合,可以匹配特定范围内的类型。在使用通配符时可以指定其上界或下界。通过添加上界或下界可以限制通配符表示的具体类型的范围。不包含上界或下界的通配符被称为无界通配符(unbounded wildcard)。例如,参数化类型ObjectHolder<?>表示其中包含的对象的具体类型是不确定的,可以是任何类型。在声明形式类型参数时也可以指定上下界,用来限制实例化时可用的实际类型范围。
在java中,除了枚举类型、匿名内部类型和异常类型之外,其他类型都可以添加形式类型参数,成为泛型类型。形式类型参数的名称可以是java中任何合法的标识符。一般使用单个大写字母作为形式类型参数的名称,以区别于一般的标识符。形式类型参数可以有多个,如“MyClass<S,U,V>”中声明了3个形式类型参数。不同的形式类型参数在代码中表示不同的含义。以集合类框架为例,List接口只包含一个形式类型参数<E>,表示列表中包含的元素的类型;而Map接口则包含两个形式类型参数<K,V>,分别表示映射表中条目的键和值的类型。
形式类型参数类似于一般的类型,但是两者存在一些差别。两者的共同之处在于都可以作为类型使用在某些场合,包含作为方法的参数和返回值类型、作为域和局部变量的类型声明、进行强制类型转换及作为泛型类型和泛型方法的实际类型参数。但是形式类型参数在某些情况下是不能使用的,包括不能用来创建对象和数组、不能作为父类型、不能使用在instanceof表达式中、不能使用其类型字面量、不能出现在异常处理中,以及不能出现在静态上下文中。这就意味着,如果T是形式类型参数,类似“new T()”、“new T[]”、“class MyClass extends T”、“instanceof T”、“T.class”、“catch(T)”、“static T”等都是无法通过编译的错误用法。这些限制源于java中泛型类型的实现机制,即类型擦除(type erasure)。
为了兼容J2SE 5.0之前的遗留代码,泛型类型在使用时可以不指定实际类型。如果不指定实际类型而直接使用类型声明,所得到的类型被称为原始类型(raw type)。如果在代码中直接使用ObjectHolder进行声明,则使用的是泛型类ObjectHolder的原始类型。原始类型的作用是与无法使用泛型的遗留代码进行交互。除此之外,原始类型不应该用在其它地方,否则引入泛型就变得毫无意义。使用原始类型是不安全的操作,编译器会给出相关的警告信息。
在构造方法或一般方法的声明中也可以使用形式类型参数。包含形式类型参数的方法被称为泛型方法。泛型方法与泛型类型并没有直接的关系。在一个非泛型类型中同样可以包含泛型方法。泛型类型中的泛型方法可以使用在类型中定义的形式类型参数,也可以使用自己的形式类型参数。
在使用泛型的情况下,编译器会对代码进行详细的类型检查。对于可以确定为错误的类型使用的地方,编译器会给出错误信息;对于无法判断是否正确的情况,编译器会给出警告信息,这意味着代码中存在可能的类型安全问题,开发人员需要谨慎处理这些警告。如果确定没有问题,那么可以使用“@SuppressWarnings("unchecked")”注解来抑制警告信息的输出。通常只有使用原始类型与遗留代码交互时所产生的警告信息才是可以忽略的。