1、无边界通配符
这里先看个例子:定义一个泛型类
public class Point<T> {
private T x;
private T y;
public Point(T x, T y) {
this.x = x;
this.y = y;
}
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y) {
this.y = y;
}
}
泛型类的使用如下:
public void test() {
Point<Integer> intPoint = new Point<>(12, 12);
Point<Float> floatPoint = new Point<>(12.0f, 12.0f);
}
可以看到我们创建了泛型类型为Integer、Float两个实例intPoint 和floatPoint ,如果生成n个类型的实例,就需要创建n个变量,那能不能只创建一个变量呢?
public void test2() {
Point<?> point;
point = new Point<Integer>(12, 12);
point = new Point<Float>(12.0f, 12.0f);
}
这里使用了无边界通配符?
来实现只创建一个变量的功能,无边界通配符?只能用于填充泛型变量T,?
表示可通配任意类型。下面再看个实例:
public void test() {
List<String> list1=new ArrayList<>();
List<Integer> list2=new ArrayList<>();
test2(list1); //编译失败
test2(list2); //编译失败
}
public void test2(List<Object> list) {
}
test2()函数接收的是一个List<Object>
类型的变量,向其中传入List<String>和List<Integer>
都是编译不通过的,这里我们使用通配符修改下test2()编译就能通过了
public void test2(List<?> list) {
}
2、通配符 <? extends XXX>
通配符 <? extends XXX>是指在填充泛型T的类型为XXX的子类并且包括XXX本身,看下具体的使用
public void test() {
List<String> list1=new ArrayList<>();
List<Integer> list2=new ArrayList<>();
List<Number> list3=new ArrayList<>();
test2(list1);//编译失败
test2(list2);
test2(list3);
}
public void test2(List<? extends Number> list) {
}
这里使用<? extends Number>
限定泛型的填充类型为Number的子类或Number,由于String并不是Number类型也不是其子类,所以编译失败。
使用<? extends Number>定义的变量,只能取其中的值,却不能修改
public void test2(List<? extends Number> list) {
list.add(12); //编译失败
Number number = list.get(0); //编译成功
}
上面代码我们无法通过add添加数据,但是能通过get()来取出数据,这又是为什么呢?
使用<
? extends Number
>表示填充泛型类型为Number及其子类型未知类型,因为不确定类型,所以不能通过add进行修改数据。
但是可以确定的是通过get()取出的类型一定是Number类型的。
3、通配符<? super XXX>
<? extends XXX>表示填充泛型类型为任意XXX子类的类型,<? super XXX>表示填充为任意XXX的父类。
public class Person {
}
public class Man extends Person {
}
public class Teacher extends Man {
}
public void test() {
List<Person> list1 = new ArrayList<>();
List<Man> list2 = new ArrayList<>();
List<Teacher> list3 = new ArrayList<>();
test2(list1);
test2(list2);
test2(list3); //编译失败
}
public void test2(List<? super Man> list) {
}
由于<? super Man>
表示填充泛型类型为Man及其Man的父类的类型,而Teacher是Man的子类,所以编译失败。
<? super Man>定义的变量能存不能取
public void test2(List<? super Man> list) {
Man man=new Man();
list.add(man);
Teacher teacher=new Teacher();
list.add(teacher);
Person person=new Person();
list.add(person);//编译失败
}
<? super Man>填充的类型为Man以及Man的父类的类型,所以当添加的数据为Man或者其子类类型的数据时,肯定是没问题的。而当添加的数据为Man的父类时则编译不通过,比如此时test2()函数传入的类型是List<Man>
时,list.add(person);
是会报错的。
list.get(0);
编译是不会报错的,但是通过get()取出的数据类型是不确定的,所以单纯的取出是没有什么意义的。
网友评论