泛型的本质就是参数化类型。也就是,将一个数据类型指定为参数。引入泛型有什么好处呢?
泛型可以将JDK 1.5之前在运行时才能发现的错误,提前到编译期。也就是说,泛型提供了编译时类型安全的检测机制。例如,一个变量本来是Integer类型,我们在代码中设置成了String,没有使用泛型的时候只有在代码运行到这了,才会报错。
而引入泛型之后就不会出现这个问题。这是因为通过泛型可以知道该参数的规定类型,然后在编译时,判断其类型是否符合规定类型。
1. 通配符
1.1. 类型通配符
在定义泛型类和泛型方法中使用的通配符
//定义泛型类
public class Generic<T> {
T data;
public Generic(T data) {
setData(data);
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
// 定义带返回值的泛型方法
private <T> T genericWithReturnMethod(T field) {
System.out.println(field.getClass().toString());
return field;
}
1.2. 无界通配符
无界通配符用?表示。看到这你可能会问,这不是跟T一样吗?为啥还要搞个?。他们主要区别在于,T主要用于声明一个泛型类或者方法,?主要用于使用泛型类和泛型方法。示例如下:
// 定义打印任何类型列表的函数
public static void printList(List<?> list) {
for (Object elem: list) {
System.out.print(elem + " ");
}
}
// 调用上述函数
List<Integer> intList = Arrays.asList(1, 2, 3);
List<String> stringList = Arrays.asList("one", "two", "three");
printList(li);// 1 2 3
printList(ls);// one two three
上述函数的目的是打印任何类型的列表。可以看到在函数内部,并没有关心List中的泛型到底是什么类型的,你可以将<?>理解为只提供了一个只读的功能,它去除了增加具体元素的能力,只保留与具体类型无关的功能。从上述的例子可以看出,它只关心元素的数量以及其是否为空,除此之外不关心任何事。
再反观T,上面我们也列举了如何定义泛型的方法以及如果调用泛型方法。泛型方法内部是要去关心具体类型的,而不仅仅是数量和不为空这么简单。
1.3. 上界通配符
使用<? extends T>表示,将未知的类型限制为特定类型或者该特定的类型的子类型。不能往里存,只能往外取,适合大量做获取操作的情景
public class ExtendTest {
private static int countLength(List<? extends Animal> list) {
return list.size();
}
public static void main(String[] args) {
List<Pig> pigs = new ArrayList<>();
List<Dog> dogs = new ArrayList<>();
List<Cat> cats = new ArrayList<>();
// 假装写入了数据
int sum = 0;
sum += countLength(pigs);
sum += countLength(dogs);
sum += countLength(cats);
System.out.println(sum);
}
}
class Animal {
}
class Pig extends Animal {
}
class Dog extends Animal {
}
class Cat extends Animal {
}
1.4. 下界通配符
使用<? super T>表示,将未知的类型限制为特定类型或者该特定的类型的超类型,也就是超类或者基类,适合大量做添加操作的情景
public class SuperTest {
public static void main(String[] args) {
Zoo<? super Pig> zoo=new Zoo<Animal>();
zoo.add(new Pig());
}
}
//动物园
class Zoo<T>{
void add(T animal) {}
}
class Animal {}
class Pig extends Animal {}
2. 使用建议
2.1. 上界下界使用建议
- extends 可用于返回类型限定,不能用于参数类型限定(换句话说:? extends xxx 只能用于方法返回类型限定,jdk能够确定此类的最小继承边界为xxx,只要是这个类的父类都能接收,但是传入参数无法确定具体类型,只能接受null的传入)。适合大量做获取操作的情景
- super 可用于参数类型限定,不能用于返回类型限定(换句话说:? supper xxx 只能用于方法传参,因为jdk能够确定传入为xxx的子类,返回只能用Object类接收)适合大量做添加操作的情景