Java泛型内各种参数的异同

先说下本篇随笔主要涉及到的东西(参考Java编程思想一书):

1、说明 List<Fruit> 与 List<Apple> 之间为什么是非继承关系。

2、由 1 引出的问题说明 List<? extends Fruit> 存在的必要性。

3、说明 List<? super Fruit> 与 List<? extends Fruit> 的区别及 List<? super Fruit> 存在的必要性。

4、说明 <? extends Fruit> 与 <T extends Fruit> 区别。

5、说明 原生List 与 List<?> 区别。

6、解释自限定泛型 class SelfBound<T extends SelfBound<T>>{ }。

Java编程思想(第4版) 中文清晰PDF完整版 下载见

下面将会用到的三个有继承关系的类:

class Fruit {

}

class Apple extends Fruit {

}

class Orange extends Fruit{

}

一、 List<Fruit> 与 List<Apple> 之间为什么是非继承关系。

我认为以下两个原因可以解释这个问题:

1、Java中泛型是后来引入的特性,为了兼容之前的代码,泛型是存在擦除机制的,List<Fruit> 与 List<Apple> 在擦除后的class中均为List,并不存在继承关系。

2、从逻辑上解释不能有继承关系的原因:

public void test(List<Fruit> list) {

list.add(new Orange());

}

在上面的代码中,test方法接收一个List<Fruit> 类型的list,并在此list 中插入了一个Orange对象,这个方法是没有任何问题的。现在假设List<Fruit> 与 List<Apple> 间存在继承关系,那么此方法可以接收一个List<Apple> 类型的list 参数作为方法的参数,然而之后在方法中就会在一个声明是List<Apple> 的list 中插入一个 Orange 对象,这显然是不符合逻辑的。所以 List<Fruit> 与 List<Apple> 之间应该是非继承关系。

二、 List<? extends Fruit> 存在的必要性。

由一的介绍我们可以知道 test 方法只能只能接受 List<Fruit> 而不能接受 List<Apple> 类型的 list, 现在我想写一个方法,既能接受List<Fruit> 又能接收 List<Apple> 类型的 List,应该怎么做呢? 这时就要用到 List<? extends Fruit> 来实现这个功能了。

List<? extends Fruit> 表示此 list持有的对象类型是 Fruit 或者从 Fruit 导出的类型(Apple 或者 Orange),相当于为 List 持有的对象类型规定了一个上界。

我们只需将上面的test 方法改为 :

public class TestGen {
   
    public void test(List<? extends Fruit> list) {
        /*由于传入的参数是 List<? extends Fruit> 参数的list,这个list是不能进行add 操作的。
        list.add(new Fruit());
        list.add(new Orange());
        list.add(new Apple());*/
    }
   
    public static void main(String[] args) {
       
        List<Apple> list = new ArrayList<Apple>();
        List<Orange> list1 = new ArrayList<Orange>();
        List<Fruit> list2 = new ArrayList<Fruit>();
       
        TestGen tg = new TestGen();
       
        tg.test(list);
        tg.test(list1);
        tg.test(list2);
       
    }
}

现在test 方法里的参数 变为了 List<? extends Fruit> list, 在 main 方法里可以看到此方法可以接受 List<Apple>,List<orange>,List<Fruit>多种类型的 list,实现了我们想要的功能。

但是在上述代码的 test 方法当中我们也可以看到,作为参数传入的 list 是不能进行 add 操作的,无论 add 的是什么类型,这是为什么呢?

原来由于传入的 List 类型是 List<? extends Fruit> list, 在JDK源码中可以看到 List的 add 方法的泛型参数就变为了 <? extends Fruit>,编译器并不能了解这里需要 add 的是哪个具体子类型,因此它

不会接受任何类型的Fruit,编译器将直接拒绝对参数列表中涉及通配符的方法的调用。

那么我们怎么才能做到向含有通配符的泛型限制的list 中做插入操作呢? 这时我们就要用到 <? super Fruit>参数。

三、List<? super Fruit> 与 List<? extends Fruit> 的区别及 List<? super Fruit> 存在的必要性。

由二可知, 要想向含有通配符的泛型限制的list 中做插入操作, 此泛型限制必须为  <? super someClass>。 前面已经说到,List<? extends Fruit> 为此List 可以持有的对象类型规定了一个上

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/d626154442925f9c128c0db6a20e7f11.html