前段时间看《Java编程思想》泛型时对 <? extends T>与<? super T>很懵逼,接着看到泛型与集合的更蒙蔽,随后又翻开《码出高效》时,对这些知识点才恍然大悟,发篇博客记录下
List、List<Object>、List<?> 的三者的区别以及 <? extends T>与<? super T> 的区别
List、List<Object>、List<?>
- List :完全没有类型限制和赋值限定。
- List<Object> :看似用法与List一样,但是在接受其他泛型赋值时会出现编译错误。
- List<?>:是一个泛型,在没有赋值前,表示可以接受任何类型的集合赋值,但赋值之后不能往里面随便添加元素,但可以remove和clear,并非immutable(不可变)集合。List<?>一般作为参数来接收外部集合,或者返回一个具体元素类型的集合,也称为通配符集合。
代码验证:(感觉用博客的页面插入代码效果不好,在此处贴了图片,代码在文章末尾补上)
<? extends T>与<? super T>List 最大的问题是只能放置一种类型,如果随意转变类型的话,就是破窗理论,泛型就失去了意义。为了放置多种受泛型约束的类型,出现了 <? extends T>与<? super T> 两种语法。简单来说, <? extends T> 是Get First,适用于,消费集合元素的场景;<? super T>是Put First,适用于,生产集合元素为主的场景。
- <? extends T> :可以赋值给任意T及T的子类集合,上界为T,取出来的类型带有泛型限制,向上强制转型为T。null 可以表示任何类型,所以null除外,任何元素都不得添加进<? extends T>集合内。
- <? super T> : 可以复制T及任何T的父类集合,下界为T。再生活中,投票选举类似于<? super T>的操作。选举投票时,你只能往里投票,取数据时,根本不知道时是谁的票,相当于泛型丢失。
<? extends T>的场景是put功能受限,而<? super T>的场景是get功能受限。
代码示例如下(以加菲猫、猫、动物为例,说明extends和super的详细语法差异0):
在代码第二段中的23行编译时报错:Type mismatch: cannot convert from List to List<? extends Cat> , 因为List赋值给List时会编译出错,因为能赋值给<? extends Cat>的类型,只有Cat和它的子类。因为<? extends Cat>泛型信息表示的是,此笼子(容器)内的全部都是猫,而List笼子(容器)内可能住着毒蛇,鳄鱼、豹猫(猫的天敌)、大型猛禽等动物,不能把它们放同一个笼子(容器)里。 26,27行中,把List赋值给 <? extends T>与<? super T> 都是可以的。在第三段代码的36、36、37行均编译出错:
The method add(capture#1-of ? extends Cat) in the type List<capture#1-of ? extends Cat> is not applicable for the arguments (Animal) 无法进行add操作,这是因为除null外,任何元素都不能添加进<? extends T>集合中。但<?super Cat>可以往里面添加元素,但只能添加Cat自身即其子类对象,如上面代码中的41、42行。因为猫的笼子中只能关猫,不能关其他动物,如代码中的40行。在上面代码中的第四段中,所有的 List<? super T>集合 都能执行get方法返回元素,但是泛型丢失,只能返回object对象,如上面代码中的46、47、48行。List<? extends T>集合 可以返回带类型的元素,但只能返回Cat自身及其父类对象,因为子类对象被擦除了,如代码中的50到54行。
附:
第一张图片中的代码import java.util.ArrayList;import java.util.List;public class TestArrayList { public static void main(String[] args) { //第一段:泛型出现之前集合定义方式 List a1 =new ArrayList(); a1.add(new Object()); a1.add(new Integer(10)); a1.add(new String("string")); //第二段:把a1引用赋值给a2,(a2与a1的区别是增加了泛型限制) List
第二张图片中的代码
import java.util.ArrayList;import java.util.List;class Animal{}class Cat extends Animal{}class Garfield extends Cat{}//用动物,猫,加菲猫的继承关系说明extends与super在集合中的意义public class AnimalCatGarfield { public static void main(String[] args) { //第一段:声明第三个依次继承的集合:Object>动物>猫>加菲猫 三个泛型集合可以理解为三个不同的笼子 Listanimal = new ArrayList (); //动物 List cat = new ArrayList (); //猫 List garfield = new ArrayList (); //加菲猫 animal.add(new Animal()); cat.add(new Cat()); garfield.add(new Garfield()); //第二段:测试赋值操作 以Cat为核心,因为它既有子类又有父类 //下行编译出错。只能赋值Cat或Cat子类集合 List extendsCatFromAnimal = animal; List superCatFromAnimal = animal; List extendsCatFromCat = cat; List superCatFromCat = cat; List extendsCatFromGarfield = garfield; //下行编译出错。只能赋值Cat或着Cat父类集合 List superCatFromGarfield = garfield; //第三段:测试add方法 //下面三行中所有的 都无法进行add操作,编译出错 extendsCatFromCat.add(new Animal()); extendsCatFromCat.add(new Cat()); extendsCatFromCat.add(new Garfield()); //下行编译出错。只能添加Cat或者Cat的子类集合。 superCatFromCat.add(new Animal()); superCatFromCat.add(new Cat()); superCatFromCat.add(new Garfield()); //第四段:测试get方法 //所有的super操作能够返回元素,但是泛型丢失,只能返回object对象 Object object1 = superCatFromCat.get(0); Animal object = superCatFromCat.get(0);//Type mismatch: cannot convert from capture#8-of ? super Cat to Animal Cat object3 = superCatFromCat.get(0);// //以下extends操作能够返回元素 Animal catExtends3 = extendsCatFromCat.get(0); Object catExtends2 = extendsCatFromCat.get(0); Cat catExtends1 = extendsCatFromCat.get(0); //下行编译错误。虽然Cat集合从Garfield赋值而来,但类型擦除后,是不知道的 Garfield cat2 = extendsCatFromGarfield.get(0); }}