? 通配符类型

  • <? extends T> 表示类型的上界,? 表示参数化类型的可能是 T 或是 T 的子类
  • <? super T> 表示类型下界,? 表示参数化类型是此 T 类型的超类型 (父类型),直至 Object

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

  • 比如,我们现在定义:List<? extends T>首先你很容易误解它为继承于T的所有类的集合,你可能认为,你定义的这个List可以用来put任何T的子类,那么我们看下面的代码:
  • List<? extneds Father> 表示 “具有任何从 Son 继承类型的列表”,编译器无法确定 List 所持有的类型,所以无法安全的向其中添加对象。可以添加 null,因为 null 可以表示任何类型。所以 List 的 add() 不能添加任何有意义的元素,但是可以接受现有的子类型 List 为赋值。
  • 你也许试图这样做:
  • 即使你指明了为 Son 类型,也不能用 add() 添加一个 Son 对象。
  • List 中为什么不能加入 Father 类和 Father 类的子类,分析如下:
  • List<? extneds Father> 表示上限是 Father,下面这样的赋值是合法的,并且如果让 List<? extends Father> 支持 add() 的话
  • List<? extends Father> list1 = new ArrayList<Father>();
    List<? extends Father> list2 = new ArrayList<Son>();
    List<? extends Father> list3 = new ArrayList<LeiFeng>();// 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?编译器在看到后面用 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<? extneds Father> 里什么都放不进去。
  • List<? extends Father> list 不能进行 add(),但是这种形式还是很有用的,虽然不能使用 add(),但是可以在初始化的时候指定不同的类型。比如:
  • 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 T>

  • // 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 类的最终祖先类。但这样的话,元素的类型信息就全部丢失了。

PECS原则

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

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

总结

  • extends 可用于返回类型限定,不能用于参数类型限定(换句话说:? extends xxx 只能用于方法返回类型限定,JDK 能够确定此类的最小继承边界为xxx,只要是这个类的父类都能接收,但是传入参数无法确定具体类型,只能接受 null 的传入)。
  • super 可用于参数类型限定,不能用于返回类型限定(换句话说:? supper xxx 只能用于方法传参,因为 JDK 能够确定传入为xxx的子类,返回只能用 Object 类接收)。
  • ? 既不能用于方法参数传入,也不能用于方法返回。
  • 注意:
  • 1、List<Object> o = new ArrayList<Long>();        // 会报错
    • 不同于数组 Object[] o,Long[] o,因为 List<Type1> 与 List<Type2> 不互为子类型或超类型
  • 2、泛型方法的使用
    • 定义泛型方法语法格式如下:
    • 这就是所谓的泛型方法,意思是这个方法所自己定义的泛型,在调用时,根据参数类型确定它的类型。
    • 调用泛型方法语法格式如下:

Java中的上界与下界(? extends/super T)相关推荐

  1. java数组的下界,Java泛型_上界extends_下界super

    Java泛型_上界extends_下界super ? 通配符类型 extends T> 表示类型的上界,表示参数化类型的可能是T或是T的子类 super T> 表示类型下界(Java C ...

  2. java 泛型的上界和下界

    1.上界 Java 泛型的上界用于限制泛型类型参数必须是某个类型的子类型,通常使用 extends 关键字来表示.下面是一个示例代码,其中 T 的上界是 Comparable<T>,表示 ...

  3. java泛型的上界和下界_java泛型中的上界(extend)和下界(super)

    泛型中上界和下界的定义 上界 extend Fruit> 下界 super Apple> 上界和下界的特点 上界的list只能get,不能add(确切地说不能add出除null之外的对象, ...

  4. java中的关键字:this与super 大详解

    文章目录 1.this 1.1. this是什么? 1.2 this调用对象中的属性.构造器和方法 2.super 2.1 super是什么? 2.2 super调用父类的属性.构造器.方法 3. t ...

  5. java contains 通配符_java 泛型通配符 extends, super

    关键字说明 ? 通配符类型 extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类 super T> 表示类型下界(Java Core中叫超类型限定),表示参数化类 ...

  6. Java中继承、this关键字、super关键字

    继承: 概述:当多个类中存在相同属性和行为时,将这些相同的内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承抽离出来的这个类即可. 子类:当某个类继承了另一个类的时候,可以把这个某类 ...

  7. java中的构造方法,this、super的用法

    1.构造方法 定义:与类同名没有返回值的方法称为构造方法: public class test1 { private String name; private int age; public test ...

  8. java中的静态块static{}及this,super,final的用法总结

    Code public class TestSquence { public static void main(String[] args) {   /*    * 给初学者的问题:把下面两行中的一行 ...

  9. 有序关系中的上界+上确界+下界+下确界

最新文章

  1. div横排 html_html中两个DIV怎么横排靠齐?
  2. linux 错误日志 __ratelimit: XXX callbacks suppressed 原因
  3. 接口中定义变量必须为public static final的原因
  4. 求解最长回文子串----Manacher 算法
  5. 漫步凸分析六——凸集的相对内点
  6. cocoapods安装bug
  7. Python入门--字符串的比较
  8. C++实现简单的文本查询
  9. 简述Git(Linux、Android~~开源)
  10. 3.修改和编译XposedBridge.jar 和 api.jar
  11. Linux源码阅读(Web在线阅读)
  12. 英语期刊写作-通往国际学术舞台的阶梯第六章答案
  13. 科学计算机怎么用10次方,计算器里10次方怎么按
  14. java画地图热力图_高德地图+热力图+AJAX(SSM)
  15. php eregireplace,eregi_replace()中特殊字符的处理方法
  16. NoteExpress 文献管理软件及使用相关问题
  17. 手把手带你学python自动化测试(一)——自动化测试环境搭建
  18. 大数据核心技术之分布式基础入门
  19. SMILES, a Chemical Language and Information System.【SMILES, 一种化学语言和信息系统。】
  20. 看京东和淘宝的地址薄设计----填写订单的场景下

热门文章

  1. 云计算学习路线教程大纲课件:部署论坛系统Discuz
  2. 数据库的数据文件和日志文件
  3. STM32的USART串口通讯程序(查询方式)
  4. 鸿蒙比安卓流畅,华为鸿蒙系统首升用户体验:流畅得不像话,专属应用体积超小!...
  5. Python3网络爬虫开发实战(第二版)
  6. mysql查看sa密码_sqlserver怎么查看sa密码
  7. 如何查看xshell保存的密码
  8. latex footnote numbering
  9. C语言:记录在主线程中停止子线程
  10. 摄影中快门、光圈、ISO之间的关系