目录

  • 1 泛型
  • 2 限定通配符与非限定通配符
    • 2.1 限定通配符
    • 2.2 非限定通配符
  • 3 PECS(Producer Extends Consumer Super)原则
    • 3.1 Producer Extends
    • 3.2 Consumer Super

1 泛型

在集合中存储对象并在使用前进行类型转换是多么的不方便。泛型防止了那种情况的发生。 它提供了编译期的类型安全,确保你只能把正确类型的对象放入集合中,避免了在运行时出现ClassCastException。

  • 不使用泛型
/*** 这样做的一个不好的是Box里面现在只能装入String类型的元素,今后如果我们需要装入Integer等其他类型的元素,* 还必须要另外重写一个Box,代码得不到复用,使用泛型可以很好的解决这个问题。*/
public class Box {private String object;public void set(String object) {this.object = object;}public String get(){return object;}
}
  • 使用泛型
public class  GenericBox<T> {// T stands for "Type"private T t;public void set(T t) { this.t = t; }public T get() { return t; }
}

2 限定通配符与非限定通配符

2.1 限定通配符

限定通配符对类型进行了限制。有两种限定通配符:

  1. <? extends T>它通过确保类型必须是T及T的子类来设定类型的上界;
  2. <? super T>它通过确保类型必须是T及T的父类设定类型的下界;

泛型类型必须用限定的类型来进行初始化,否则会导致编译错误。

2.2 非限定通配符

表示了非限定通配符,因为可以用任意类型来替代。

public class BoundaryCharExample {//查找一个泛型数组中大于某个特定元素的个数public static <T> int countGreaterThan(T[] array,T elem){int count = 0;for (T e : array) {if (e > elem) { // compiler error++count;}}return count;}/** comliler error:但是这样很明显是错误的,* 因为除了short, int, double, long, float, byte, char等原始类型,* 其他的类并不一定能使用操作符" > "* 解决 --> 使用限定通配符/边界符* */
}

使用限定通配符改进:

public class BoundaryCharExample2 {public static <T extends Comparable<T>> int countGreaterThan(T[] array,T elem){//<T extends Comparable<T>>就是通配符,类型必须是 Comparable<T>及其子类int count = 0;for (T e : array) {if (e.compareTo(elem)>0) {++count;}}return count;}
}

3 PECS(Producer Extends Consumer Super)原则

  • Producer Extends:如果你需要一个只读List,用它来produce T,那么使用? extends T;
  • Consumer Super:如果你需要一个只写List,用它来consume T,那么使用? super T;

3.1 Producer Extends

对于实现了<? extends T>的集合类只能将它视为 Producer 向外提供(get)元素, 而不能作为 Consumer 向外获取(add)元素。

public class GenericExample {public static void main(String[] args) {List<? extends Fruit> fruits = new ArrayList<Apple>();//? extends Fruit表示的是Fruit及其子类// Compile Error: can't add any type of object://fruits.add(new Apple());//fruits.add(new Orange());//fruits.add(new Fruit());//fruits.add(new Object());//fruits.add(null); // Legal but uninteresting}
}

Compile Error: can’t add any type of object:

从编译器的角度去考虑,List<? extends Fruit> fruits自身可以有多种含义:

List<? extends Fruit> fruits = new ArrayList<Fruit>();List<? extends Fruit> fruits = new ArrayList<Apple>();List<? extends Fruit> fruits = new ArrayList<Orange>();// 这里Apple和Orange都是Fruit子类
  1. 当我们尝试add一个Apple的时候,fruits可能指向new ArrayList< Orange >();
  2. 当我们尝试add一个Orange的时候,fruits可能指向new ArrayList< Apple >();
  3. 当我们尝试add一个Fruit的时候,这个Fruit可以是任何类型的Fruit,而fruits可能只想是某种特定类型的Fruit,因此编译器无法识别,报错。

应用示例:

public class GenericReading {private List<Apple> apples = Arrays.asList(new Apple());private List<Fruit> fruit = Arrays.asList(new Fruit());private class Reader<T>{ //Reader<T> 是自定义的泛型类/*T readExact(List<T> list){ return list.get(0);}*/T readExact(List<? extends T> list){// 使用通配符来解决这个问题// ? extends T 表示 T 及 T 的子类return list.get(0); //TODO :get()方法}}@Testpublic void test(){Reader<Fruit> fruitReader=new Reader<Fruit>();//Fruit f=fruitReader.readExact(apples);// 使用 readExact(List<T> list)  // Errors: List<Fruit> cannot be applied to List<Apple>.Fruit f=fruitReader.readExact(apples);//正确System.out.println(f);}
}

3.2 Consumer Super

对于实现了<? super T>的集合类只能将它视为 Consumer 向外获取(add)元素, 而不能作为 Producer 向外提供(get)元素。

从编译器的角度考虑,对于List<? super Apple> list,它可以有下面几种含义:

List<? super Apple> list = new ArrayList<Apple>();
List<? super Apple> list = new ArrayList<Fruit>();
List<? super Apple> list = new ArrayList<Object>();

当我们尝试通过list来get一个Apple的时候,可能会get得到一个Fruit,这个Fruit可以是Orange等其他类型的Fruit,因此编译器无法识别,报错。

应用示例:

public class GenericWriting {private List<Apple> apples = new ArrayList<Apple>();private List<Orange> oranges = new ArrayList<Orange>();private List<Fruit> fruit = new ArrayList<Fruit>();<T> void writeExact(List<T> list, T item) {list.add(item); //TODO :这里是 add}// ? super T // T 及 T 的父类<T> void writeWithWildcard(List<? super T> list, T item) {list.add(item);}void func1(){writeExact(apples,new Apple());writeExact(fruit,new Apple());}void func2(){writeWithWildcard(apples, new Apple());writeWithWildcard(fruit, new Apple());}@Testpublic void test(){func1();func2();}
}

JDK 8 Collections.copy() 源码:

public static <T> void copy(List<? super T> dest, List<? extends T> src) {//dest 就是 只写的 List//src 就是 只读的 Listint srcSize = src.size();if (srcSize > dest.size())throw new IndexOutOfBoundsException("Source does not fit in dest");if (srcSize < COPY_THRESHOLD ||(src instanceof RandomAccess && dest instanceof RandomAccess)) {for (int i=0; i<srcSize; i++)dest.set(i, src.get(i));} else {ListIterator<? super T> di=dest.listIterator();ListIterator<? extends T> si=src.listIterator();for (int i=0; i<srcSize; i++) {di.next();di.set(si.next());}}}

RandomAccess是一个空的、用来标记的接口;

用处是当要实现某些算法时,会判断当前类是否实现了RandomAccess接口,会选择不同的算法;

接口RandomAccess中内容是空的,只是作为标记用。比如List下的ArrayList和LinkedList。其中ArrayList实现了RandomAccess。而LinkedList没有。我们可以利用instanceof来判断哪一个是实现了RandomAccess。分辨出两个集合。其中ArrayList使用for循环遍历快,而LinkedList使用迭代器快。那么通过分辨,不同的集合使用不同的遍历方式;

Java笔记:泛型、限定通配符与非限定通配符相关推荐

  1. 限定通配符和非限定通配符_为什么我不信任通配符以及为什么我们仍然需要通配符...

    限定通配符和非限定通配符 在将子类型多态性(面向对象)与参数多态性(泛型)相结合的任何编程语言中,都会出现方差问题. 假设我有一个字符串列表,键入List<String> . 我可以将其传 ...

  2. 什么是泛型中的限定通配符和非限定通配符 ?

    限定通配符对类型进行了限制. 有两种限定通配符,一种是它通过确保类型必须是T的子类来设定类型的上界,另一种是它通过确保类型必须是T的父类来设定类型的下界.泛型类型必须用限定内的类型来进行初始化,否则会 ...

  3. Java中限定类名和非限定类名的区别

    限定类名,就是类名全称,带包路径的用点隔开,例如: java.lang.String. 非限定(non-qualified)类名也叫短名,就是我们平时说的类名,不带包的,例如:String. 非限定类 ...

  4. (Java)全限定类名和非限定类名的区别

    全限定类名:就是类名全称,带包路径的用点隔开,例如: java.lang.String. 即全限定名 = 包名+类型,又如: 这里的 T 就是类名,即非限定类名,mybatis.T 就是全限定类名 非 ...

  5. Java 全限定类名和非限定类名有何区别

    为了方便说明,这里创建一个普通Java类来做演示 package com.csdn.test;public class HelloWorld {} 1.1 对于该类来说:全限定类名就是包名.类名,即c ...

  6. java中的全限定类名和非限定类名是什么意思?

    全限定类名是指带包名的类名:(如java.lang.String) 非限定类名是不带包名的类名.(如String)

  7. JAVA集合泛型,类型擦除,类型通配符上限之类的知识点

    感觉定义要比PYTHON严谨很多,一切源于静态语言的特点吧.. 于是语法上就复杂很多,值不值得呢? 参考测试URL: http://www.cnblogs.com/lwbqqyumidi/p/3837 ...

  8. java工作笔记019---java8新特性判断非null

    技术交流QQ群[JAVA,C,.NET,BigData,AI]:170933152 对于null的处理,可以下面这样做: Optional.ofNullable(str).orElse(new Str ...

  9. 【连载】Java笔记——是了解咖啡小可爱的第一周呢

    我是灼灼,一只初学Java的大一金渐层. 向往余秀华和狄兰·托马斯的疯狂,时常沉溺于将情感以诗相寄:追逐过王尔德.王小波的文字,后陷于毛姆和斯蒂芬·金不可自拔:热爱文学的浪潮,白日梦到底却总在现实里清 ...

最新文章

  1. 物流信息管理系统MySQL设计,物流管理系统的SQL数据库设计(含代码)
  2. 10组第一次作业-现代软件工程2017成员介绍
  3. PHP脚本占用内存太多,解决方案
  4. iphone日历显示周视图_用敬业签记录放假安排 2021年放假安排日历
  5. memcached 命令_Memcached Telnet命令示例
  6. list的一些常见用法总结(实用干货收藏)
  7. java 动态读取配置文件_java读取配置文件的几种方法
  8. 栈的应用:火车调度问题
  9. 人民币大写转换工具C++实现(支持任意位金额转换)
  10. 性能测试监控TP50、TP99、TP999含义
  11. python字典中的值不允许重复_python字典中值不允许重复的原因
  12. 小米系统shell_获取linux系统信息shell | 小米的博客
  13. apache2 docker 无法访问_Docker 安装 Apache
  14. IEEE-754标准与浮点数运算
  15. 辰视智能董事长冯良炳博士接受起点机器人网专访!
  16. Metal每日分享,颜色转换滤镜效果
  17. Mac 备份 time machine开启全速备份
  18. iOS 横竖屏处理方案
  19. pip全局设置镜像源
  20. 硬件编解码开发 linux,Intel平台硬件加速视频编解码开发

热门文章

  1. 使用【PaddleSeg3D】搭建项目对肝脏进行3D分割
  2. Pagehelper使用教程
  3. python 爬虫餐饮行业 数据分析_Python爬取美团美食板块商家数据
  4. JSON格式化有问题,Jackson替换JSON格式化处理
  5. 服务器管理器如何添加共享账号,大势至共享文件管理系统如何添加账号
  6. 1024,程序员福利是这些??
  7. 在Mac上如何轻松下载来自浏览器的软件或内容
  8. 【SPOJ】7258. Lexicographical Substring Search(后缀自动机)
  9. 龙卷风收音机客户端电台数据解密算法
  10. 频谱分析幅值单位_知否知否?常用振动诊断方法——包络分析和阶次分析