mp.weixin.qq.com/s?src=11&ti…
概述
在Java中,通配符类型List等价于List<? extends Object>,List<? extends Animal>则代表其可以被赋值为List<Cat>或List<Dog>等其子类,List<? super Cat>代表其可以被List<Animal>赋值。
对于采用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>(),这种情况下只能返回Object类型。