[Java基础扫盲]--反射与泛型--(5)泛型
Author:赵志乾
Date:2019-05-04
Declaration:All Right Reserved!!!
1、什么是泛型
所谓的泛型就是编写模板代码来适应任意类型,从而避免一些繁琐的类型转化,且能够让编译器在编译阶段进行类型检查。在使用泛型时,其继承关系中要保持泛型参数不变。例如:ArrayList<Integer>和List<Integer>有继承关系,而ArrayList<Integer>和List<Number>没有继承关系。
在使用泛型时,需要将泛型参数T替换为需要的引用类型;当不指定泛型参数类型时,编译器会给出警告,且只能将泛型参数视为Object类型。
2、编写泛型
按照某种类型去编写类;
标记其中所有的特定类型;
将特定类型替换为T,并在声明处使用<T>;
注意:泛型类型<T>不能用于静态方法,否则会出现编译错误,因为编译器无法在静态字段或静态方法中使用泛型类型<T>。静态方法可以单独改写为泛型方法,其使用的泛型参数和类的泛型参数不是同一个,例如使用另一个类型<K>
public class Pair<T>{
private T first;
private T second;
public Pair(T first, T second){
this.first = first;
this.second = second;
}
public T getFirst(){
return first;
}
public T getSecond(){
return second;
}
public static <K> Pair<K> create(K first,K second){
return new Pair<K>(first,second);
}
}
注意:定义泛型时,也可以使用多个泛型参数,如<T,K,U>
3、泛型擦除
编译器会将类型<T>视为Object,只不过在编译的过程中会依据泛型参数<T>实现安全的强制类型转换。故泛型这一该概念在虚拟机中是不存在的。由于编译器会将泛型参数视为Object,所以泛型参数不能是基本类型。
由于编译后,编译器会将泛型擦除,所以在运行时,无法取得带泛型的Class实例。因为他们对应的都是泛型参数为Object类型时的Class实例。
由于泛型参数会被擦除为Object,所以T类型不可以直接实例化。其在源码中进行实例化时必须借助于Class<T>。如:
public class Pair<T>{
private T first;
private T second;
public Pair(Class<T> clazz){
first = clazz.newInstance();
second = clazz.newInstance();
}
}
4、extends通配符
由于泛型参数在继承体系中不可变,即相同的泛型参数才具有继承关系。为了解决这一问题,引入了extends通配符,其使得泛型参数由单一类型扩展至该单一类型及其子类型。方法参数使用通配符下:
public class Pair<T>{
.....
}
public class PairHelper{
public static double add(Pair<? extends Number> p){
//可以调用get方法,但不可以调用set方法;因为我们无法确定传入的类型具体是什么类型
Number first = p.getFirst();
Number second = p.getSecond();
return first.doubleValue() + second.doubleValue();
}
}
类型定义使用extends通配符如下,其限定了定义Pair<T>时只能时使用Number或Number的子类替换泛型参数:
public class Pair<T extends Number>{
private T first;
private T second;
public Pair(T first, T second){
this.first = first;
this.second = second;
}
public T getFirst(){
return first;
}
public T getSecond(){
return second;
}
}
5、super通配符
super通配符是extends通配符的互补,其在方法参数中使用时,表明泛型参数由单一类型扩展至单一类型及其超类。
public class Pair<T>{
.....
}
public class PairHelper{
public static void set(Pair<? super Integer> p, Integer first,Integer second){
//可以调用set方法,但不可以调用get方法;因为无法确定传入的泛型类型
p.setFirst(first);
p.setSecond(second);
}
}
同理,super通配符用于类型定义时,其限定了泛型参数只能使用指定单一类型及其超类。
注意:<? extends T> 允许调用方法获取T的引用,而<? super T>允许调用方法传入T的引用。
6、无限定通配符
<?>称为无限定通配符,包含了super通配符和extends通配符的限制,所以无限定通配符很少使用。