对于java 泛型 编译时处理,运行时擦除的特点理解

  • 对于编译时处理

    • 在使用泛型相关的类或方法时,如果声明时的类型和具体使用时的类型不一致则直接会编译不通过
  • 对于运行时擦除
    • 当在运行时对两个相同类型但泛型的具体类型不同时,在运行时实际都是操作的相同的类型,而不会体现出来泛型的的具体类型;

    

  1. public static void main(String[] args){
  2.  
  3.     List a = Collections.emptyList();
  4. List b = Collections.emptyList();
  5. // true , 对于不包含泛型初始化的list实际都是使用的相同的实例数据
  6. LOGGER.info(String.valueOf(a == b));
  7.  
  8. List<String> a1 = Collections.emptyList();
  9. boolean v = a == a1;
  10. // true, 对于 a 和 a1 两个参数的类型实际是不同的,但在运行时实际是对于a和a1的类型实际都是相同的List类型; 可以通过 javap -v 类名 来查看编译后的class文件
  11. LOGGER.info(String.valueOf(v));
  12.  
  13. List<Integer> b1 = Collections.emptyList();
  14. // 由于a1和b1的泛型的具体类型不一致,因此在编译时不会通过
  15. // boolean v1 = a1 == b1
  16. }

  对于泛型的显式限定和隐式限定区别

  1. public static void castQuestion() {
  2. // 在执行实例化操作时,实际已经隐式限定了当前对象的类型
  3. // 在执行具体操作时,虽然根据变量的限定符显式定义,但在实际使用中就会抛出错误
  4. Container<StringBuilder> stringContainer = new Container("1");
  5. StringBuilder element = stringContainer.getElement();
  6.  
  7. }
  8.  
  9. // 自定义内部容器类,类型为泛型
  10. // 单界限操作: E extends CharSequence,这里限定了泛型的类型只能为CharSequence的子级
  11. public static class Container<E extends Serializable> {
  12. private E element;
  13.  
  14. public Container(E element) {
  15. this.element = element;
  16. // 可以看到当前元素的实际类型
  17. System.out.println(element.getClass().getTypeName());
  18. }
  19.  
  20. // 方法
  21. public E getElement() {
  22. return element;
  23. }
  24.  
  25. public void setElement(E element) {
  26. this.element = element;
  27. }
  28. }

  可以看到在指定 new 时未显式指定对象元素类型,但通过调用有参构造方法实际已限定了当前对象的element元素类型;

  虽然对象变量显式限定了当前变量的泛型,对于操作方法实际是根据调用者的具体泛型类型进行限制,因此可以看到 "StringBuilder element = stringContainer.getElement();" 返回值类型为 StringBuilder;

  而由于对象中的实际类型为String类型,当将String类型强制赋值为Integer类型数据时,就会抛出ClassCastException

由于泛型存在编译时校验,运行时擦写的特点,因此为了保证运行时也提供泛型类型校验, 在Collections中提供了 checked*的工具类,在执行操作时保证了运行时的类型校验

  1. public void collectionGenericType() {
  2. List<Integer> integers = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
  3. // 由于泛型存在编译时校验,运行时擦写
  4. List noGenericTypeList = integers;
  5. System.out.println(noGenericTypeList == integers);
  6. // 虽然 noGenericTypeList 引用了 integers
  7. // 运行时泛型擦写 List<Integer> -> List<Object> -> List
  8. // 因此可以写入任意类型的数据
  9. noGenericTypeList.add("A");
  10. // 由于数据读取时需要进行类型转换(转换为泛型的指定类型)因此会抛出ClassCastException
  11. // integers.forEach(System.out::println);
  12. // 而对于noGenericTypeList由于没有泛型的约束,因此读取数据是都是按照Object类型处理
  13. noGenericTypeList.forEach(System.out::println);
  14. // 在转换时并没有执行类型检查因此支持直接转换
  15. List<Integer> castList = new ArrayList<>(noGenericTypeList);
  16. // 因此为了避免类型擦写导致的异常,因此需要使用包装类型工具类
  17. // 当转换为checkedList时并不会进行类型校验
  18. /**
  19. * Wrapper(装饰器)模式的使用
  20. * Collections.checked*接口弥补了 泛型运行时擦写的不足
  21. * 强类型: 编译时泛型强制类型检查,运行时利用Collections.checked*强类型检查
  22. */
  23. List<Integer> checkedList = Collections.checkedList(castList, Integer.TYPE);
  24. // 会生成新的数据
  25. System.out.println(checkedList == castList);
  26.  
  27. noGenericTypeList = checkedList;
  28. // 对于checkedList在执行添加时,会执行类型校验,因此会直接抛出错误
  29. noGenericTypeList.add("B");
  30. }

  

java 泛型学习随笔的更多相关文章

  1. Java泛型学习笔记 - (七)浅析泛型中通配符的使用

    一.基本概念:在学习Java泛型的过程中, 通配符是较难理解的一部分. 主要有以下三类:1. 无边界的通配符(Unbounded Wildcards), 就是<?>, 比如List< ...

  2. Java泛型学习笔记--Java泛型和C#泛型比较学习(一)

    总结Java的泛型前,先简单的介绍下C#的泛型,通过对比,比较学习Java泛型的目的和设计意图.C#泛型是C#语言2.0和通用语言运行时(CLR)同时支持的一个特性(这一点是导致C#泛型和Java泛型 ...

  3. java泛型学习(2)

    一:深入泛型使用.主要是父类和子类存在泛型的demo /** * 父类为泛型类 * @author 尚晓飞 * @date 2014-7-15 下午7:31:25 * * * 父类和子类的泛型. * ...

  4. Java泛型学习---第二篇

    泛型学习第一篇 1.泛型之擦拭法 泛型是一种类似"模板代码"的技术,不同语言的泛型实现方式不一定相同. Java语言的泛型实现方式是擦拭法(Type Erasure). 所谓擦拭法 ...

  5. java泛型学习(1)

    java泛型(Generices Type) --->概念:泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和 ...

  6. Java泛型学习一

    Java泛型 所谓泛型,就是变量类型的参数化.泛型是java1.5中引入的一个重要特征,通过引入泛型,可以使编译时类型安全,运行时更少抛出ClassCastException的可能.一提到参数化,最熟 ...

  7. Java 泛型学习总结

    前言 Java 5 添加了泛型,提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型. 泛型的本质是参数化类型,可以为以前处理通用对象的类和方法,指定具体的对象类型.听起来有点抽象, ...

  8. Java泛型学习笔记 - (六)泛型的继承

    在学习继承的时候, 我们已经知道可以将一个子类的对象赋值给其父类的对象, 也就是父类引用指向子类对象, 如: Object obj = new Integer(10); 这其实就是面向对象编程中的is ...

  9. java 泛型学习

    http://blog.csdn.net/archie2010/article/details/6232228 学习集合框架的时候经常用hasmap<Integer,Integer>就是泛 ...

随机推荐

  1. 使用 codeblocks 编写C++ udp组播程序遇到的问题

    编译错误 会出现好多undefined reference to'WSAStartup to@8之类的错误,都是undefind开头的 解决方法: Settings -> Compiler se ...

  2. JavaScript学习系列博客_27_JavaScript 遍历数组

    遍历数组 - 遍历数组就是将数组中元素都获取到 - 一般情况我们都是使用for循环来遍历数组: - 使用forEach()方法来遍历数组(不兼容IE8) forEach()方法需要一个回调函数(由我们 ...

  3. Robot Framework(7)——接口测试

    一.准备工作 1.安装requests工具(2.22.0) 下载地址:https://pypi.org/project/requests/ 安装方式: 1>下载压缩文件,解压,目录切到解压目录, ...

  4. 第6篇scrum冲刺(5.26)

    一.站立会议 1.照片 2.工作安排 成员 昨天已完成的工作 今天的工作安排 困难 陈芝敏   研究云开发,更新了登录模块,把用户的信息传入数据库了  学习云开发,云函数调用以及数据的前后端传递  遇 ...

  5. Camera学习--光源

    进入CV 领域,视频图像的成像,最前端的camera,camera的sensor 以及影响成像质量的光源,噪声等因素是绕不开的问题. 那么今天就从成像的光源说起. 标准光源(Standard Ligh ...

  6. muduo源码解析4-exception类

    exception class exception:public std::exception { }; 作用: 实现了一个异常类,继承于std::exception,主要用于实现打印线程调用栈信息. ...

  7. vscode 安装go插件失败后,最简单的方法

    vscode 安装go插件 参考: https://github.com/goproxy/goproxy.cn/blob/master/README.zh-CN.md https://goproxy. ...

  8. AcWing243一个简单的整数问题2(树状数组+差分+前缀和规律)

    题目地址:https://www.acwing.com/problem/content/244/ 题目描述: 给定一个长度为N的数列A,以及M条指令,每条指令可能是以下两种之一: 1.“C l r d ...

  9. Vue + axios + SpringBoot 2实现导出Excel

    Vue + axios + SpringBoot 2实现导出Excel 1. 前端js代码-发送Http请求 /** * 文件下载 * @param url 下载地址 * @param fileNam ...

  10. os.path获取当前路径及父路径

    import os pwd = os.getcwd() print("当前目录: " + pwd) father_path_method1 = os.path.dirname(pw ...