示例如下:
package test;
public class Gen {
private T ob;
public Gen(T o) {
ob = o;
}
public T getOb() {
return ob;
}
public void showType() {
System.out.println("Type of T is " + ob.getClass().getName());
}
}
package test;
public class GenDemo {
public static void main(String[] args) {
/*
* 在java7及以后的版本中,构造函数中可以省略泛型类型,就像下面这样,
* 只有一对尖括号<>
*/
//Gen iob = new Gen<>(88);
Gen iob = new Gen(88);
iob.showType();
int v = iob.getOb();
System.out.println("value: " + v);
}
}
我们来分析一下上面示例的部分代码。
首先,注意这行代码声明泛型类Gen的方式:
public class Gen {......}
其中,T是类型参数的名称。这个名称是实际类型的占位符,当创建对象时,将实际类型传递给Gen。因此在Gen中,只要需要类型参数,就使用T。注意T被包含在<>中,只要是声明类型参数,就需要在尖括号中指定。因为Gen使用类型参数,所以Gen是泛型类,也称为参数化类型。
接下来使用T声明对象ob:
private T ob;
前面解释过,T是将在创建Gen对象时指定的实际类型的占位符。因此,ob是传递给T的那种实际类型的对象。例如,如果将String传递给T,ob将是String类型。
现在分析Gen的构造函数:
public Gen(T o) {ob = o;}
注意参数o的类型是T,这意味着o的实际类型取决于创建Gen对象时传递给T的类型。此外,因为参数o和成员变量ob的类型都是T,所以在创建Gen对象时,它们将具有相同的类型。
还可以使用类型参数T指定方法的返回类型,就像getOb()方法那样:
public T getOb() {return ob;}
因为ob也是T类型,所以ob的类型和getOb()方法指定的返回类型是兼容的。
showType()方法通过对Class对象调用getName()方法来显示T的类型。而这个Class对象是通过对ob调用getClass()方法返回的。getClass()方法是由Object类型定义的,因此该方法是所有类的成员。该方法返回一个Class对象,这个Class对象与调用对象所属的类对应。Class定义了getName()方法,该方法返回类名的字符串表示形式。
GenDemo类演示了泛型化的Gen类。它首先创建整形版本的Gen类:
Gen iob = new Gen<>(88);
我们仔细分析这句代码。首先看声明部分,注意类型Integer是在Gen后面的尖括号中指定的。因此,Integer是传递给Gen的类型参数。这有效地创建了Gen的一个版本。在该版本中,对T的所有引用都被转换为对Integer的引用。因此对于这个声明,ob是Integer类型,并且getOb()方法的返回类型也是Integer。
在继续之前,必须先说明的是,java编译器实际上没有创建不同版本的Gen类,或者说没有创建任何其他泛型类。尽管这样认为是有帮助的,但是实际情况并非如此。相反,编译器擦除所有泛型类型信息,将之替换为必需的类型转换,从而使代码的行为好像是创建了特定版本的Gen类一样。因此,在程序中实际上只有一个版本的Gen类。擦除泛型类型信息的过程被称为:类型擦除。
我们接着看上面这句代码的创建对象部分,注意在调用Gen构造函数时,仍然指定了类型参数Integer。这是必需的,因为将为其赋值的对象(在此为iob)的类型是Gen。因此,new返回的引用也必须是Gen类型。如果不是的话,就会产生编译错误。如果在java7中,你可以省略Integer,只留下一对类括号<>,省略的类型可以从变量的类型推断中得出。因为iob是Gen类型,所以不能引用Gen类型的对象,这种类型检查是泛型的主要优点之一,因为可以确保类型安全。
泛型只使用对象
当创建泛型类的实例时,传递过来的类型参数必须是类。不能使用基本类型,比如:int、char。对于Gen,可以将任何类传递给T,但是不能将基本类型传递给类型参数T。当然,不能使用基本类型并不是一个严重的限制,因为可以使用类型封装器封装基本类型。java的自动装箱和拆箱机制使得类型封装器的使用是透明的。
基于不同类型参数的泛型类型是不同的
对特定版本的泛型类型的引用和同一泛型类型的其他版本不是类型兼容的,这是关于泛型类型方面更需要理解的关键一点。如:Gen与Gen不是类型兼容的,尽管它们都是Gen类型的。