泛型通配符与泛型方法

马煜
  • 9

这里我定义了三个类,它们之间存在以下的继承关系:

class Animal{}
class Cat extends Animal{}
class Dog extends Animal{}

问题1:泛型通配符和泛型方法在使用上下界时有什么区别吗?

public void fun(ArrayList<? extends Animal> list) { } //通配符
public <T extends Animal> void fun(ArrayList<T> list) { }//泛型方法

问题2:为什么使用这种泛型通配符和泛型方法的上下界的形式时不能添加到元素呢?

public void fun(ArrayList<? extends Animal> list) {
    list.add(new Dog()); //报错:Required ?; Provide Dog
}

public <T extends Animal> void fun(ArrayList<T> list) {
    list.add(new Animal());
    list.add(new Cat());
    list.add(new Dog());
}

image.png

image.png

问题3:为什么使用泛型方法也不可以向其中添加以及修改元素

public <T> void fun1(ArrayList<T> arrayList) {
    arrayList.add(new Cat());
    arrayList.add(new Animal());
}

image.png

问题4:
知乎这篇关于泛型的文章是不是错误的呢?可是在我的问题3中报错。是JDK版本问题吗?(我的是1.8)
image.png
https://zhuanlan.zhihu.com/p/...

回复
阅读 1k
2 个回答

个人观点,欢迎补充纠正。

问题1:是由区别的,如果声明在方法上,仅仅只是声明这个泛型在方法范围内的边界,其本质依然是T,而参数则是真正的的? extend Animal。所以带来的结果自然也不相同,方法上是可以方法,而对List的泛型做限定,而参数则是对泛型做限定,而无法使用方法。

问题2:关于这个问题,如何使用extendsuper 有一句口诀,当泛型是消费者的时候,使用extend ,当泛型是生产者的时候,使用super。泛型不具备协变性,List<Object> list = new Arraylist<Integer>();是不能编译的,extend 如果可能添加,直接导致泛型出现混乱,而get确能保证安全的向上转型;具体可以参考我以前的博客:https://blog.csdn.net/perfect...

问题3:这个问题和问题1重复了,明白了问题1,这个问题就解决了。

居然还有问题4:问题4 是正确的,这里的关键是List<Object>List<? extend Object> 注意这两种表述是存在差异的。前者泛型已经确定,后者是不确定的,确定的是可以添加的,比如add(1.2),而后面是不确定,意思是我也不知道会是那个class。所以不能添加。

问题1:
void fun(ArrayList<? extends Animal> list) 可以传 List<Dog> 也可以传 List<Cat>
<T extends Animal> void fun(ArrayList<T> list) 差不多,主要区别是可以拿到 T 来做一些事情

问题2:
因为传进来的参数可能是 List<Cat> 也可能是 List<Dog>
于是你在方法里面 add(new Dog()) 是不行的,因为可能不是一个 List<Dog>

问题3:
因为不确定类型
<T> void fun1(ArrayList<T> arrayList)T 在运行时可能是各种类型
所以理所当然不能添加/修改

问题4:
没问题,例子相当于把一个 List<? extends Animal> 添加到 List<Animal> 中,是没有问题的

宣传栏