Java泛型模仿自C++中的模板,在使用和语法上也极为相似,但功能上却比C++差强人意了很多。
泛型的声明:
<T>写在类名或接口名后,在本类中所有地方出现的T被限定为同一种类型,T代表的具体类型在调用该类处指定,如:
interface Data<Type> {} //定义泛型接口
class TableData implements Data<Integer> {} //实现该接口的同时确定类型参数的具体类型
class TableData<T> implements Data<T> {} //也可以这样,将类型具体化交给上一级调用者。
new TableData<Integer>(); //具体化了Data接口中的Type与TableData类中的T。
可以通过extends向上限定与super向下限定一个泛型变量的类型范围,如:
interface Data<Type extends Number> {} //限定了接口泛型参数只支持Number即其子类
//由于上一层限定了范围,调用者也必须限定,限定范围可以缩小但不能增加
class TableData<T extends Integer> implements Data<T> {}
new TableData<String>(); //编译是失败的,因为String并不是Integer即其子类。
引用在声明时泛型的限定:
当一个引用在声明的时候,类型后具体化了泛型参数类型,那么指向的对象具体化这个类型必须一样,或是不指定任何泛型参数类型如:
class TableData<T> implements Data<T> {}
TableData<Number> td = new TableData() //不指定泛型参数编译通过
TableData<Number> td = new TableData<Number>() //泛型参数一样编译通过
TableData<Number> td = new TableData<Integer>() //虽然是子父关系,但不一样仍然编译失败
可以使用通配符限定具体类型的范围,如:
TableData<?> td = new TableData<String>() //表示该引用接收任意泛型类型限定的对象
TableData<? extends Number> td = new TableData<Integer>()//编译通过
TableData<? super Number> td = new TableData<Object>() //编译通过
//函数接收一个TableData对象,但这个对象的泛型限定必须是Number即其子类型,或是不做任何泛型限定
void func(TableData<? extends Number> td)
泛型函数:
泛型可以定义在函数的返回类型前,如<T> void func(T t),可以通过调用函数时传递的参数类型具体化T的类型,也可以通过泛型函数的返回接收类型来确定T的类型。函数同样可以通过extends向上限定与super向下限定一个泛型变量的类型范围,如:
<T extends Number> void func(T t) //此时t只能为一个Number即其子类对象
注意:当类泛型参数名与类中函数泛型参数中重复时,在该函数范围内以函数的泛型参数具体类型为主。
静态函数内不能使用该类的泛型参数。只能使用函数泛型。
另外在明确泛型类型参数的时候不可以使用基本数据类型,如new TableData<int>() 是不允许的,可以将int替换为基本数据封装类。
Java中泛型只能用在编译时期,当文件编译成class文件后,所有泛型标记自动被剔除。也就是说无法实现动态的类型参数化。