泛型是Java5.0的新特性之一。
1.定义简单泛型类
泛型类就是具有一个或多个类型变量的类。下面是一个泛型类
在Java库中使用变量E表示集合的元素类型,K和V分别表示关键字与值的类型,T表示“任意类型”(需要的时候还可以用临近的字母U和S表示)。
2.带类型参数的泛型方法
注意:
1.泛型方法的声明,类型变量放在修饰符后面,返回值类型前面。
2.泛型方法可以定义在普通类中,也可以定义在泛型类中。
3.泛型变量的限定
有的时候,类或方法需要对类型变量进行限定。比如说将类型变量限制为实现某个接口的类,或继承某个父类的子类。
注意:1)限定使用的关键字为extends,<T extends BoundingType>表示T应该是绑定类型的子类型,T和绑定类型可以是类也 可以是接口。使用关键字extends的原因,更接近于子类概念。
2)一个类型变量允许有多个限定,限定类型使用'&'分隔,例如:<T extends Comparable&Serializable>
3)一个类型变量的限定中,允许有多个接口超类型,但至多只能有一个类。如果用一个类作为限定,他必须是限定列表 的第一个。
4)为提高效率,应该将标签接口(即没有方法的接口)放在限定列表的末尾。
4.类型擦除
无论何时定义一个泛型类型,都自动提供了一个相应的原始类型。原始类型的名字就是删去类型参数后的泛型类型名。擦除类型变量,并替换为限定类型(使用第一个限定类型替换,无限定类型的变量使用Object)。
因为GenericClass没有限定类型,所以类型变量被替换成Object。
5.泛型使用的约束与局限性
1)不能用基本类型实例化类型参数
没有GenericClass<int>,只有GenericClass<Integer>。当然,他的原因是类型擦除。擦除后GenericClass类含有Object类型的域,而Object不能存储int值。
2)运行时类型查询只适用于原始类型
试图查询一个对象是否属于某个泛型类型时,若使用instanceof会得到一个编译错误,若使用强制类型转换,会得到一个警告。一般使用getClass方法得到原始类型。
3)不能创建参数化类型的数组
例如:GenericClass<String>[] table = new GenericClass<String>[10];//error
4)Varargs警告
添加注解@SuppressWarnings("unchecked"),在JAVA SE7中还可以用@SafeVarargs直接标注产生varargs警告的方法,抑制这个警告。
5)不能实例化类型变量
不能使用像new T(...),new T[...]或T.calss这样的表达式中的类型变量。
例如:T first = new T();//error
类型擦除后T会变成Object,在Java SE8以后,最好的解决方法就是让调用者提供一个构造器表达式,
例如: GenericClass<String> g = GenericClass.makeGenericClass(String::new);
makeGenericClass方法接收一个函数式接口Supplier<T>,表示一个无参而且返回值位T的函数:
public static <T> GenericClass<T> makeGenericClass(Supplier<T> constr){
return new GenericClass<>(constr.get(),constr.get())
}
6)不能构造泛型数组
7)泛型类的静态上下文中类型变量无效
不能在静态域或方法中引用类型变量。
8)不能抛出或捕获泛型类的实例
泛型类不能继承Exception/Throwable。
catch子句中不能使用类型变量T。
异常规范中使用类型变量是允许的,以下方法是合法的:
public <T extends Throwable> void doWork(T t) throws T//ok
9)可以消除对受查异常的检查
10)注意擦除后的冲突