泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是安全简单。
在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。
一个泛型类,就是具有一个或多个类型变量的类。在定义泛型类时,要将类型变量用尖括号(<>)括起来,并放在类名的后面:
/**
* Generic class with limited type variable
*
* @author xzm
*
* @param
*/
class Employee & Serializable> {
T employeeA;
T employeeB;
public Employee(T employeeA, T employeeB) {
super();
this.employeeA = employeeA;
this.employeeB = employeeB;
}
public T getEmployeeA() {
return employeeA;
}
public void setEmployeeA(T employeeA) {
this.employeeA = employeeA;
}
public T getEmployeeB() {
return employeeB;
}
public void setEmployeeB(T employeeB) {
this.employeeB = employeeB;
}
}
/**
* Generic class without limited type variable
*
* @author xzm
* @param
* Generic type
*/
class Pair {
private T first;
private T second;
public Pair() {
first = null;
second = null;
}
public Pair(T f, T s) {
first = f;
second = s;
}
public T getFirst() {
return first;
}
public void setFirst(T first) {
this.first = first;
}
public T getSecond() {
return second;
}
public void setSecond(T second) {
this.second = second;
}
}
Pair和Employee类引入了一个类型变量"T"。如果我们要使用多个类型变量,只需要在<>尖括号中添加,同时各个类型变量之间以","隔开。在使用泛型类时,我们要指定一个明确的类型来初始化泛型类。Java中的集合框架基本都使用了泛型。例如,当我们要使用ArrayList存储String对象时,我们通常会这样进行初始化:
List list = new ArrayList();
list.add("aaa");
泛型在编译期间会提供类型检查,例如往list中添加Integer对象,将会报一个编译错误。这无疑提供了代码的安全性。
Java SE中,ArrayList是这样声明的:
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
它使用了类型变量E,我们在初始化ArrayList时,给它指定一个明确的类型:String。同理,我们使用Pair类:
Pair pair = new Pair();
当我们给某个泛型类指定特定类型时,从代码来说,它有很好的可读性,因为应用于泛型类的类型显而易见。
在Java SE7及以后的版本中,构造函数中可以省略泛型类型。同样的Pair定义,我们可以这样:
Pair pair = new Pair<>();
省略的类型,编译器可以很简单地从变量的类型推断得出。两种写法都符合要求,我们可以自由选择。
泛型方法既可以出现在非泛型类中,也可以出现在泛型类中。下面我们在非泛型类中进行介绍。看如下的一个示例:
class GenericMethod {
/**
*
* @param param1
* generic type
* @param param2
* generic type
* @return
*/
public static String genericMethod(T param1, U param2) {
return "param1: " + param1.toString() + " param2: " + param2.toString();
}
}
在GenericMethod类中,我们定义了一个泛型方法genericMethod(),它使用了两个类型变量:T和U,返回值为String。尖括号包裹的类型变量:"
泛型方法的使用,跟泛型类基本一致,提供具体的类型即可:
GenericMethod.genericMethod(1.3, 2);
泛型中, 类型变量只代表某种引用类型,不包括基本数据类型。此例中,可以传入基本类型变量是因为现在Java提供了自动拆装箱机制,1.3和2会自动地被转换为对应的包装类型对象。当然,泛型方法的返回值也可为类型变量类型。
泛型接口的情形跟泛型类是基本类似的,就不在多做介绍,我们只举一个示例,我们经常使用的Comparable接口定义:
public interface Comparable {
public int compareTo(T o);
}
以上主要介绍了泛型的一些基本定义和使用方法。要很好地理解泛型,我们还必须了解泛型中的"类型擦除",这一部分将在第二篇中再作介绍。