转:https://blog.csdn.net/qq_25337221/article/details/81669630

PECS原则

最后看一下什么是PECS(Producer Extends Consumer Super)原则,已经很好理解了:

  • 频繁往外读取内容的,适合用上界Extends。
  • 经常往里插入的,适合用下界Super。

总结

  • extends 可用于返回类型限定,不能用于参数类型限定(换句话说:? extends xxx 只能用于方法返回类型限定,jdk能够确定此类的最小继承边界为xxx,只要是这个类的父类都能接收,但是传入参数无法确定具体类型,只能接受null的传入)。
  • super 可用于参数类型限定,不能用于返回类型限定(换句话说:? supper xxx 只能用于方法传参,因为jdk能够确定传入为xxx的子类,返回只能用Object类接收)。
  • ? 既不能用于方法参数传入,也不能用于方法返回。

带有super超类型限定的通配符可以向泛型对象中写入,带有extends子类型限定的通配符可以向泛型对象读取。

? 通配符类型

  1. - <? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类;

  2. <? super T> 表示类型下界(Java Core中叫超类型限定),表示参数化类型是此类型的超类型(父类型),直至Object;

上界<? extends T>不能往里存,只能往外取

比如,我们现在定义:List<? extends T>首先你很容易误解它为继承于T的所有类的集合,你可能认为,你定义的这个List可以用来put任何T的子类,那么我们看下面的代码:

import java.util.LinkedList;

import java.util.List;

public class test {

public static void main(String[] args) {

List<? extends Father> list = new LinkedList<>();

list.add(new Son());

}

}

class Human{

}

class Father extends Human{

}

class Son extends Father{

}

class LeiFeng extends Father {

}

list.add(new Son());这行会报错:The method put(Son) is undefined for the type List<capture#1-of ? extends Father>

List<? extends Father> 表示 “具有任何从Son继承类型的列表”,编译器无法确定List所持有的类型,所以无法安全的向其中添加对象。可以添加null,因为null 可以表示任何类型。所以List 的add 方法不能添加任何有意义的元素,但是可以接受现有的子类型List 赋值。

你也许试图这样做:

List<? extends Father> list = new LinkedList<Son>();

list.add(new Son());

即使你指明了为Son类型,也不能用add方法添加一个Son对象。

list中为什么不能加入Father类和Father类的子类呢,我们来分析下。

List<? extends Father>表示上限是Father,下面这样的赋值都是合法的

List<? extends Father> list1 = new ArrayList<Father>();

List<? extends Father> list2 = new ArrayList<Son>();

List<? extends Father> list3 = new ArrayList<LeiFeng>();

如果List<? extends Father>支持add方法的话:

  • list1可以add Father和所有Father的子类;
  • list2可以add Son和所有Son的子类;
  • list3可以add LeiFeng和所有LeiFeng的子类。

下面代码是编译不通过的:

list1.add(new Father());//error

list1.add(new Son());//error

原因是编译器只知道容器内是Father或者它的派生类,但具体是什么类型不知道。可能是Father?可能是Son?也可能是LeiFeng,XiaoMing?编译器在看到后面用Father赋值以后,集合里并没有限定参数类型是“Father“。而是标上一个占位符:CAP#1,来表示捕获一个Father或Father的子类,具体是什么类不知道,代号CAP#1。然后无论是想往里插入Son或者LeiFeng或者Father编译器都不知道能不能和这个CAP#1匹配,所以就都不允许。

所以通配符<?>和类型参数的区别就在于,对编译器来说所有的T都代表同一种类型。比如下面这个泛型方法里,三个T都指代同一个类型,要么都是String,要么都是Integer。

public <T> List<T> fill(T... t);

但通配符<?>没有这种约束,List<?>单纯的就表示:集合里放了一个东西,是什么我不知道。

所以这里的错误就在这里,List<? extends Father>里什么都放不进去。

List<? extends Father> list不能进行add,但是,这种形式还是很有用的,虽然不能使用add方法,但是可以在初始化的时候一个Season指定不同的类型。比如:

List<? extends Father> list1 = getFatherList();//getFatherList方法会返回一个Father的子类的list

另外,由于我们已经保证了List中保存的是Father类或者他的某一个子类,所以,可以用get方法直接获得值:

List<? extends Father> list1 = new ArrayList<>();
Father father = list1.get(0);//读取出来的东西只能存放在Father或它的基类里。
Object object = list1.get(0);//读取出来的东西只能存放在Father或它的基类里。
Human human = list1.get(0);//读取出来的东西只能存放在Father或它的基类里。
Son son = (Son)list1.get(0);

下界

//super只能添加Father和Father的子类,不能添加Father的父类,读取出来的东西只能存放在Object类里

List<? super Father> list = new ArrayList<>();

list.add(new Father());

list.add(new Human());//compile error

list.add(new Son());

Father person1 = list.get(0);//compile error

Son son = list.get(0);//compile error

Object object1 = list.get(0);

因为下界规定了元素的最小粒度的下限,实际上是放松了容器元素的类型控制。既然元素是Father的基类,那往里存粒度比Father小的都可以。出于对类型安全的考虑,我们可以加入Father对象或者其任何子类(如Son)对象,但由于编译器并不知道List的内容究竟是Father的哪个超类,因此不允许加入特定的任何超类(如Human)。而当我们读取的时候,编译器在不知道是什么类型的情况下只能返回Object对象,因为Object是任何Java类的最终祖先类。但这样的话,元素的类型信息就全部丢失了。

java中? extends T 和? super T解析的更多相关文章

  1. Java中<? extends T>和<? super T>的理解

    ? 通配符类型 - <? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类; <? super T> 表示类型下界(Java Core中叫超类型限 ...

  2. java中的this与super的区别

    java中的this与super的区别 1. 子类的构造函数如果要引用super的话,必须把super放在函数的首位 代码如下: class Base { Base() { System.out.pr ...

  3. Java 中extends与implements使用方法

    Java 中extends与implements使用方法 标签: javaclassinterfacestring语言c 2011-04-14 14:57 33314人阅读 评论(7) 收藏 举报 分 ...

  4. JAVA中extends 与implements区别

    JAVA中extends 与implements有啥区别?1. 在类的声明中,通过关键字extends来创建一个类的子类.一个类通过关键字implements声明自己使用一个或者多个接口.extend ...

  5. JAVA中extends 与implements有啥区别?

    JAVA中extends 与implements有啥区别?1. 在类的声明中,通过关键字extends来创建一个类的子类.一个类通过关键字implements声明自己使用一个或者多个接口.extend ...

  6. java中extends与implements的区别

    学了java很久了,久不用之后给同学解决一个java问题的时候,就卡在这个标题上了. 下面是java中extends与implements的区别: 1. 在类的声明中,通过关键字extends来创建一 ...

  7. java中构造方法和方法super/this超类与子类中初始化顺序

    java中构造方法和方法全面解析 我相信大多说人都对构造方法.方法不陌生,而且很了解,但我也相信有很多像我这样的没有一个很好很清晰很全面的认识,今天就把它整理如下,希望能给大家带来点方便与帮助,也希望 ...

  8. java中的this和super的作用和异同和C++中调用父类的构造函数

    来源于:http://www.cnblogs.com/hasse/p/5023392.html 这几天看到类在继承时会用到this和super,这里就做了一点总结,与各位共同交流,有错误请各位指正~ ...

  9. Java中关键词之this,super的使用

    一.this关键词介绍. 说明:该知识点仅仅限于本人对其了解的范围. package com.study.java.oop; /** * 核心点:"this是指向对象本身的一个指针" ...

  10. Java中的this和super

    在Java中有两个非常特殊的变量:this和super,这两个变量在使用前都是不需要声明的.this变量使用在一个成员函数的内部,指向当前对象,当前对象指的是调用当前正在执行方法的那个对象.super ...

随机推荐

  1. 专心研发CPU?传Intel外包芯片组设计给祥硕

    在本届台北电脑展上,AMD除了推出7nm锐龙3000处理器之外,还发布了新一代平台X570芯片组,首发了消费级PCIe 4.0技术支持.X570芯片组因为技术难度更高,所以这一代是AMD亲自出手设计研 ...

  2. vscode remote-ssh 远程开发

    https://www.jianshu.com/p/7fcd995a408d 连是连上了,但每隔几十秒就会断开重连,不知道是什么情况...

  3. SpringFramework中的BeanWrapper丶PropertyEditor

    BeanWrapper是org.springframework.beans包下的一个借口,对应的实现类为BeanWrapperImpl,提供对应的get/set方法,并且设置属性的可读性和可写性. p ...

  4. maven参数详解

    setting.xml主要用于配置maven的运行环境等一系列通用的属性,是全局级别的配置文件:而pom.xml主要描述了项目的maven坐标,依赖关系,开发者需要遵循的规则,缺陷管理系统,组织和li ...

  5. element-ui + redis + mongo + nuxt

    用户注册: let {username,password} = req.body; let u = await UserModel.findOne({username}); if(u){ res.js ...

  6. Vue原理解析——自己写个Vue

    Vue由于其高效的性能和灵活入门简单.轻量的特点下变得火热.在当今前端越来越普遍的使用,今天来剖析一下Vue的深入响应式原理. tips:转自我的博客唐益达博客,此为原创.转载请注明出处,原文链接 一 ...

  7. python中一个简单的webserver

     python中一个简单的webserver 2013-02-24 15:37:49 分类: Python/Ruby 支持多线程的webserver   1 2 3 4 5 6 7 8 9 10 11 ...

  8. redis优雅的批量删除key

    redis优雅的批量删除key 近期在处理redis的故障中,发现需要删除大量的历史数据(也是bigkeys),好在符合正则表达式.要不然就很痛苦,这也体现了在设计key的时候遵循规范带来的维护好处之 ...

  9. Nginx静态文件服务器配置方法

    在Java开发以及生产环境中,最常用的web应用服务器当属Tomcat,尽管这只猫也能够处理一些静态请求,例如图片.html.样式文件等,但是效率并不是那么尽人意.在生产环境中,我们一般使用Nginx ...

  10. 【JZOJ1282】打工

    题目 分析 显然,有一个结论, 在有效的方案中,第i位的数一定小于等于i. 所以,设\(f_{i,j,k}\)表示,做到第i位,前i位的最大值为j,前i位是否与输入的序列的前i位相等. 转移方程随便搞 ...