为啥要有通配符?
通配符是用来 解决泛型无法协变的问题 的。协变指的就是如果 Student是Person的子类,那么 List<Student> 也应该是 List<Person> 的子类。但是泛型是不支持这样的父子类关系。
通配符和泛型的区别?
1、运用泛型:
public static <T> void print1(ArrayList<T> list){//此时传入T后就知道T是什么类型的
for (T x:list) {//再遍历的时候就知道了list里的类型,直接用T接收就行
System.out.println(x);
}
}2、运用通配符:
public static void print2(ArrayList<?> list){//此时也可以传一个任意类型,但传完之后也不知道传的是啥类型的
for (Object x:list) {//由于不知道传了啥类型,所以用Object接收 System.out.println(x);}
}
通配符上界(extends)
语法:<? extends 上界><? extends Number>//可以传入的实参类型是Number或者Number的子类
示例:public static void printAll(MyArrayList<? extends Number> list) {...}// 以下调用都是正确的printAll(new MyArrayList<Integer>());printAll(new MyArrayList<Double>());printAll(new MyArrayList<Number>());// 以下调用是编译错误的printAll(new MyArrayList<String>());printAll(new MyArrayList<Object>());
通配符的上界-父子类关系
MyArrayList
<?
extends
Number
>是
MyArrayList
<
Integer
>
或者
MyArrayList
<
Double
>
的父类类型
MyArrayList
<?>
是 MyArrayList
<?
extends
Number
>
的父类型
MyArrayList
<?> 默认上界是Object
如何解决协变问题? 让父类类型能够接收子类
假设有如下关系 :AnimalCat extends AnimalDog extends Animal根据以上的关系,写一个方法,打印一个存储了Animal或者Animal子类的list 。
法一:(错误方法)
public static void print(List<Animal> list) {//用泛型}不可以解决问题,因为泛型里 List<Cat> list 不是List<Animal> list 的子类法二:public static <T extends Animal> void print2(List<T> list) {//用有约束的泛型for (T animal : list) {System.out.println(animal);}}此时约定好了要传的类型一定是Animal的子类,此时用泛型也可以法三:public static void print3(List<? extends Animal> list) {//用通配符实现for (Animal ani : list) {System.out.println(ani);}}这样做的好处是不管传的是啥,传过去都让它变成Animal的子类,传哪个并不知道,因此后面要用Animal;
与法二的区别,法二假设要传Dog,后面就知道了传的是Dog,后面直接用Dog(T)接受
通配符的上界的特点
ArrayList<Integer> arrayList1 = new ArrayList<>(); ArrayList<Double> arrayList2 = new ArrayList<>(); List<? extends Number> list1 = arrayList1;//因为list可以引用Number或者Number的子类 List<? extends Number> list2 = arrayList1; list1.add(0,1);//会报错,因为此时既可以引用Integer的list,又可以引用Double的list,此时就不知道现在引用的是哪个了 list1.add(1,10,9);//会报错 Number o=list1.get(0);//可以用Number来接收
总结:
对于通配符的上界,不适合写入数据,适合读取数据(读取数据时,使用父类来接收)
通配符下界(super)
语法:<? super 下界><? super Integer>//代表 可以传入的实参的类型是Integer或者Integer的父类类型
示例 :public static void printAll(MyArrayList<? super Integer> list) {...}// 以下调用都是正确的printAll(new MyArrayList<Integer>());printAll(new MyArrayList<Number>());printAll(new MyArrayList<Object>());// 以下调用是编译错误的printAll(new MyArrayList<String>());printAll(new MyArrayList<Double>());
通配符下界的父子类关系
MyArrayList
<?
super
Integer
>
是
MyArrayList
<
Integer
>
的父类类型
MyArrayList
<?>
是
MyArrayList
<?
super
Integer
>
的父类类型
通配符下界的
特点
class Person{ } class Student extends Person{ } public static void main(String[] args) { ArrayList<? super Person> arrayList1=new ArrayList<>();//arrayList1引用的一定是Person的父类 arrayList1.add(new Person());//此时在往里面添加元素时可以添加Person或者Person的子类 arrayList1.add(new Student()); Person person=arrayList1.get(0);//会报错,因为当前不知道读取的是哪个子类 Object o=arrayList1.get(0);//此时要想获取元素,必须用Object接收 }
总结:对于通配符的上界,适合写入数据,不适合读取数据