深入解析Java泛型通配符类型

版权声明:本文为博主原创文章 https://blog.csdn.net/qq_21845263/article/details/89490491

概述

在Java中,通配符类型List<?>等价于List<? extends Object>,List<? extends Animal>则代表其可以被赋值为List或List等其子类,List<? super Cat>代表其可以被List赋值。

对于采用extends形式的,主要用于读取(get),写入(set)就只能写入null

对于采用super形式的,主要用于写入,读取丢失了类型信息只能读取到Object,这就违背了泛型的初衷了。

前提

假设我们有以下代码,定义了各个类的继承顺序,本文主要探讨的是为什么Java要这样设计。

class Scratch {
    public static void main(String[] args) {
    }

    static class Animal {
    }

    static class Cat extends Animal {

    }

    static class WhiteCat extends Cat {
    }

    static class Dog extends Animal {
    }
}

在不使用泛型通配符的情况下,泛型之间是不存在继承性

考虑如下代码:

List<Cat> cats = new ArrayList<>();
List<Animal> animals = cats; //这句无法通过编译

为什么Java被设计成这样?

我们假设以上代码可以通过编译,那么我们可以构造如下代码:

List<Animal> animals = new ArrayList<>();
List<Object> objs = animals;

考虑我们使用List.add()时,就会发现一些不合理的部分了

objs.add(new Integer(1));

这显然不合理,原本用于存放Animal类型的List居然可以存放Integer,所以要禁止泛型间的继承性

使用泛型通配符时

extends限定

List<Cat> cats = new ArrayList<>();
List<? extends Animal> list = cats;
Animal animal = list.get(0);
list.add(null);

以上代码是可以通过编译的,我们可以看到,对于get,我们可以获取到的类型是Animal,这是因为所有的子类型都首先是个父类型,最终我们获取到的类型信息是父类类型。

为什么add不能有其它类型除了null

因为list无法确定原来的类型是什么,经历list=cats之后,list并不知道其持有的引用原来是保存Cat类型的,那能不能放入Animal类型呢,显然不行,一个Animal不一定是Cat,同理Animal的子类以及父类都不行,所以为了类型安全,那就啥类型也不然添加。

super限定

List<Animal> animals = new ArrayList<>();
List<? super Cat> list1 = animals;
list1.add(new WhiteCat());
Object object = list1.get(0);

以上代码也是可以通过编译的。对于add,我们可以添加任何继承Cat的类(同时也包括Cat本身),因为list1所指向的List其泛型可以是自身及其父类,add限制合情合理,添加的都是子类。

为什么get只能获得Object类型

其实获得Object类型就代表不知道其是什么类型。主要原因在于list1不知道原来泛型的类型是什么。

考虑极端情况list1 = new ArrayList(),这种情况下只能返回Object类型。


欢迎访问我的 个人网站(主要), Github, CSDN(主要), 博客园, 简书, 掘金, 知乎, 微信公众号:HelloVant(主要)

本文采用 知识共享 署名-非商业性使用-禁止演绎(CC by-nc-nd) 4.0 国际 许可协议 授权

猜你喜欢

转载自blog.csdn.net/qq_21845263/article/details/89490491