前段时间看《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<Object> a2 =a1;
a2.add(new Object());
a2.add(new Integer(20));
a2.add(new String("string2"));
a2.add(25);
//List<Object> 接受其他泛型赋值时,会报异常(因为在下面例子中List<Integer>不能转为List<Object>)
List<Integer> aint = new ArrayList<Integer>();
List<Object> a22 =aint;//Type mismatch: cannot convert from List<Integer> to List<Object> //第三段:把a1引用赋值给a3,(a3与a1的区别是增加了泛型<Integer>)
List<Integer> a3 = a1; //此时如果遍历a3则会报类型转换异常ClassCastException
a3.add(new Integer(20));
//下面两行编译出错,不允许增加非Integer类型进入集合
a3.add(new Object());//The method add(Integer) in the type List<Integer> is not applicable for the arguments (Object)
a3.add(new String("string2")); //第四段:把a1引用赋值给a4,a4与a1的区别是增加了通配符
List<?> a4 = a1;
//允许删除和清除元素
a4.remove(0);
a4.clear();
//编译错误,不允许添加任何元素
a4.add(new Object());//The method add(capture#3-of ?) in the type List<capture#3-of ?> is not applicable for the arguments (Object)
a4.add(new Integer(20));
a4.add(new String("string2"));
}
}

第二张图片中的代码

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>动物>猫>加菲猫 三个泛型集合可以理解为三个不同的笼子
List<Animal> animal = new ArrayList<Animal>(); //动物
List<Cat> cat = new ArrayList<Cat>(); //猫
List<Garfield> garfield = new ArrayList<Garfield>(); //加菲猫 animal.add(new Animal());
cat.add(new Cat());
garfield.add(new Garfield()); //第二段:测试赋值操作 以Cat为核心,因为它既有子类又有父类
//下行编译出错。只能赋值Cat或Cat子类集合
List<? extends Cat> extendsCatFromAnimal = animal;
List<? super Cat> superCatFromAnimal = animal; List<? extends Cat> extendsCatFromCat = cat;
List<? super Cat> superCatFromCat = cat; List<? extends Cat> extendsCatFromGarfield = garfield;
//下行编译出错。只能赋值Cat或着Cat父类集合
List<? super Cat> superCatFromGarfield = garfield; //第三段:测试add方法
//下面三行中所有的<? extends T>都无法进行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);
}
}

一文搞懂List 、List<Object>、List<?>的区别以及<? extends T>与<? super T>的区别的更多相关文章

  1. 夯实Java基础系列6:一文搞懂抽象类和接口,从基础到面试题,揭秘其本质区别!

    目录 抽象类介绍 为什么要用抽象类 一个抽象类小故事 一个抽象类小游戏 接口介绍 接口与类相似点: 接口与类的区别: 接口特性 抽象类和接口的区别 接口的使用: 接口最佳实践:设计模式中的工厂模式 接 ...

  2. 一文搞懂所有Java集合面试题

    Java集合 刚刚经历过秋招,看了大量的面经,顺便将常见的Java集合常考知识点总结了一下,并根据被问到的频率大致做了一个标注.一颗星表示知识点需要了解,被问到的频率不高,面试时起码能说个差不多.两颗 ...

  3. 一文搞懂RAM、ROM、SDRAM、DRAM、DDR、flash等存储介质

    一文搞懂RAM.ROM.SDRAM.DRAM.DDR.flash等存储介质 存储介质基本分类:ROM和RAM RAM:随机访问存储器(Random Access Memory),易失性.是与CPU直接 ...

  4. 基础篇|一文搞懂RNN(循环神经网络)

    基础篇|一文搞懂RNN(循环神经网络) https://mp.weixin.qq.com/s/va1gmavl2ZESgnM7biORQg 神经网络基础 神经网络可以当做是能够拟合任意函数的黑盒子,只 ...

  5. 一文搞懂 Prometheus 的直方图

    原文链接:一文搞懂 Prometheus 的直方图 Prometheus 中提供了四种指标类型(参考:Prometheus 的指标类型),其中直方图(Histogram)和摘要(Summary)是最复 ...

  6. Web端即时通讯基础知识补课:一文搞懂跨域的所有问题!

    本文原作者: Wizey,作者博客:http://wenshixin.gitee.io,即时通讯网收录时有改动,感谢原作者的无私分享. 1.引言 典型的Web端即时通讯技术应用场景,主要有以下两种形式 ...

  7. 一文搞懂vim复制粘贴

    转载自本人独立博客https://liushiming.cn/2020/01/18/copy-and-paste-in-vim/ 概述 复制粘贴是文本编辑最常用的功能,但是在vim中复制粘贴还是有点麻 ...

  8. 三文搞懂学会Docker容器技术(中)

    接着上面一篇:三文搞懂学会Docker容器技术(上) 6,Docker容器 6.1 创建并启动容器 docker run [OPTIONS] IMAGE [COMMAND] [ARG...] --na ...

  9. 三文搞懂学会Docker容器技术(下)

    接着上面一篇:三文搞懂学会Docker容器技术(上) 三文搞懂学会Docker容器技术(中) 7,Docker容器目录挂载 7.1 简介 容器目录挂载: 我们可以在创建容器的时候,将宿主机的目录与容器 ...

随机推荐

  1. SYN3305A型 小型时统设备

       SYN3305A型  小型时统设备 产品概述 SYN3305A型小型时统设备是由西安同步电子科技有限公司精心设计.自行研发生产的一款高准确度的锁相石英频率标准.内装OCX0恒温晶体振荡器,利用G ...

  2. Python 爬虫从入门到进阶之路(六)

    在之前的文章中我们介绍了一下 opener 应用中的 ProxyHandler 处理器(代理设置),本篇文章我们再来看一下 opener 中的 Cookie 的使用. Cookie 是指某些网站服务器 ...

  3. Redis 学习笔记(篇一):字符串和链表

    本次学习除了基本内容之外主要思考三个问题:why(为什么).what(原理是什么).which(同类中还有哪些类似的东西,相比有什么区别). 由于我对 java 比较熟悉,并且 java 中也有字符串 ...

  4. python 之 面向对象基础(定义类、创建对象,名称空间)

    第七章面向对象 1.面向过程编程 核心是"过程"二字,过程指的是解决问题的步骤,即先干什么再干什么 基于该思想编写程序就好比在编写一条流水线,是一种机械式的思维方式 优点:复杂的问 ...

  5. 面试还不知道BeanFactory和ApplicationContext的区别?

    接口 BeanFactory 和 ApplicationContext 都是用来从容器中获取 Spring beans 的,但是,他们二者有很大不同 我看到过很多问 BeanFactory 和 App ...

  6. MyBatis 源码分析

    MyBatis 运行过程 传统的 JDBC 编程查询数据库的代码和过程总结. 加载驱动. 创建连接,Connection 对象. 根据 Connection 创建 Statement 或者 Prepa ...

  7. Spark —— 高可用集群搭建

    一.集群规划 这里搭建一个3节点的Spark集群,其中三台主机上均部署Worker服务.同时为了保证高可用,除了在hadoop001上部署主Master服务外,还在hadoop002和hadoop00 ...

  8. spring 5.x 系列第11篇 —— 整合memcached (xml配置方式)

    文章目录 一.说明 1.1 XMemcached客户端说明 1.2 项目结构说明 1.3 依赖说明 二.spring 整合 memcached 2.1 单机配置 2.2 集群配置 2.3 存储基本类型 ...

  9. WebGL 着色器偏导数dFdx和dFdy介绍

    本文适合对webgl.计算机图形学.前端可视化感兴趣的读者. 偏导数函数(HLSL中的ddx和ddy,GLSL中的dFdx和dFdy)是片元着色器中的一个用于计算任何变量基于屏幕空间坐标的变化率的指令 ...

  10. 并发编程-concurrent指南-回环栅栏CyclicBarrier

    字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行. java.util.concurrent.CyclicBarrier 类是一种同步机制,它能够对处理一些算法的线程实现同步 ...