Java中的泛型
什么是泛型?
所谓泛型,实质上就是不使用具体数据类型(int、double、float 等),而是使用一种通用类型(T、K、V、E 等)来进行程序设计的方法,该方法可以避免因为程序中数据类型的不同,而被迫重复编写大量相同业务逻辑的代码。
为什么要使用泛型?
- 代码的重用:使用了泛型程序设计意味着编写的程序代码可以被很多不同类型的对象重用。
- 泛型的好处:
- 使代码具有更好的可读性:如ArrayList
,其中的类型参数String就指明集合内存储的元素类型; - 获取元素时不用再进行类型转换:编译器也可以通过类型参数推算出返回类型
- 使我们编写的程序更安全:因为泛型的类型参数指明了集合能接收的对象类型,放入错误的类型将报编译错误。
- 使代码具有更好的可读性:如ArrayList
- 总结:泛型的类型参数可以使程序具有更好的可读性和安全性。
定义简单的泛型类和方法
一个简单的泛型类
public class Pair<T> {
private T first;
private T second;
public Pair(){}
public Pair(T first, T second){
this.first = first;
this.second = second;
}
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;
}
}
定义一个泛型方法
public class ObjFactory {
public static <T> T getObj(Class<T> clazz) throws Exception {
return clazz.newInstance();
}
}
类型变量的限定
有些时候,类或方法需要对类型变量加以约束。如下面的例子:
public static <T extends Comparable> T min(T... arr){
if (arr == null || arr.length == 0) return null;
T min = arr[0];
for (int i = 1; i < arr.length; i++){
if(min.compareTo(arr[i]) > 0){
min = arr[i];
}
}
return min;
}
注意min方法的代码内部。变量min类型为T, 这意味着它可以是任何一个类的对象。怎么才能确保T所属的类有compareTo方法呢?
解决这个问题的方案是将T限制为实现了Comparable接口(只含一个方法compareTo的标准接口)的类。可以通过对类型变量T设置限定(bound) 实现这一点:
public static <T extends Comparable> T min(T... arr)
泛型代码和虚拟机
虚拟机中没有泛型类型对象——所有的对象都属于普通类。
类型擦除
无论在何时定义了一个泛型类型,都会自动提供一个相应的原始类型(raw type)。
原始类型的名字就是删去类型参数后的泛型类型名,擦除(eerased)类型变量,并替换为限定类型(无限定类型的变量用Object替换)。
例如上面的泛型类Pair的原始类型如下:
//删除类型参数
public class Pair {
//擦除类型变量,并替换为限定类型,无限定类型的变量用Object替换
private Object first;
private Object second;
public Pair(){}
public Pair(Object first, Object second){
this.first = first;
this.second = second;
}
}
原始类型使用第一个限定的类型变量来进行替换,如果没有给定限定就使用Object替换。
public class Interval <T extends Comparable & Serializable> implements Serializable{
private T lower;
private T upper;
}
类型擦除后
public class Interval implements Serializable{
//
private Comparable lower;
private Comparable upper;
}
使用泛型的约束与限制
- 不能使用基本类型实例化类型参数
其原因是类型擦除,类型擦除之后,原始类型中含有Object域,而Object不能存储double值。 - 运行时类型查询只适用于原始类型
if(a instanceof Pair<String>)//错误
if(a instanceof Pair<T>)//错误 - 不能创建参数化类型的数组
Pair<String>[] pairs = new Pair<String>[10];//不可以
Pair<String>[] pairs = new Pair[10];//可以
- 不能在静态域或静态方法中引用泛型类型变量。例如,下面的骚操作将无法施展:
public class Pair<T> {
private T first;
private T second;
public static T filed;
public static T getFiled(){
return filed;
}
泛型类的继承规则
在使用泛型类时,需要了解一些有关继承和子类型的准则。考虑一个类和一个子类,如Employee和Manager。Pair<Manager>是Pair<Employee> 的一个子类吗? 答案是“不是”, 或许人们会感到奇怪,奇怪我也不想再继续写了。
通配符类型
固定的泛型类型使用起来并没有那么有意思,Java的设计者发明了一种巧妙的(仍然是安全的)“解决方案”:通配符类型。
通配符类型中,允许参数变化。
通配符类型子类限定
比如,通配符类型Pair<? extends Employee>,表示任何泛型Pair类型,它的类型参数必须是Employee的子类,像Pair<Manager>,但Pair<String>不可以。
通配符类型超类限定
Pair<? super Manager>,这个通配符限制为 Manager 的所有超类型。
常见的用法是作为一个函数式接口的参数类型。例如,
Collection接口有一个方法:
default boolean removeIf(Predicate<? super E> filter)
这个方法会删除所有满足给定谓词条件的元素。
如果你希望传入一个Predicate

浙公网安备 33010602011771号