>版权申明】非商业目的注明出处可自由转载

博文地址:https://blog.csdn.net/ShuSheng0007/article/details/89789849

出自:shusheng007

相关文章: 秒懂Java泛型 秒懂Java类型(Type)系统

@[toc]

概述

曾几何时你是否在简历中写道:精通Java、精通xx、精通oo 等等的字眼,说出来不怕丢人,本人也干过。现在想想真是惊叹于当时的“勇气”,或者是自大?亦或是无知? 如果为了找工作,写上这些还是情有可原的,毕竟大部分筛选简历的是那些毛都不懂的HR,但是你内心中还是要对精通二字怀有敬畏之情!不为别的,就为了你可以虚怀若谷,勇往直前,如果你都认为自己精通了,估计就会坐井观天,止步不前了。

今天让我们聊聊Java泛型擦除那些事,就当做自己的学习笔记吧。

泛型

概念及使用方法不是本文的重点,如果对泛型的概念和使用有疑问请出门左转查看 秒懂Java泛型。本文也不想争论Java泛型实现机制的优劣,因为研究不够深入,不敢妄言。我们就现在Java泛型的实现方式聊聊其中的一些事。

泛型信息运行时擦除

Java 泛型在运行时擦除这一点应该大部分开发者都有一定了的解,那么什么叫擦除了呢?就是泛型信息在源代码里面有,而在生成的字节码里面就没有了,之所以这么干主要是为了向下兼容老的java版本。那问题就很明了了,字节码中都不存在泛型的信息了,而我们的运行时对象是JVM从字节码装载进来的,那自然也就没有泛型信息了。看下面代码

ArrayList<String> arrayString=new ArrayList<String>();     ArrayList<Integer> arrayInteger=new ArrayList<Integer>();     System.out.println(arrayString.getClass()==arrayInteger.getClass());

上面代码输入结果为 true,可见通过运行时获取的类信息是完全一致的,泛型类型被擦除了!

至此,我以为我精通了Java 泛型,直到有一天我写了 秒懂Java类型(Type)系统 这篇文章,我变的很困惑。不是说Java的泛型是运行时擦除的吗,为什么我还能通过反射获取到泛型信息,例如我有如下代码

public class TypeTest<T>{public List<T> list;
}

我通过反射可以获取到这list的声明类型 java.util.List<T>

Field list = TypeTest.class.getField("list");
Type genericType1 = list.getGenericType();
System.out.println("参数类型1:" + genericType1.getTypeName());

输出

参数类型1:java.util.List<T>

是不是很奇怪,不是被擦除了吗?这是为什么?

泛型信息声明时保留

通过仔细思考上面对泛型擦除的原理的理解:泛型擦除是泛型信息在源代码里面有,而在生成的字节码里面就没有了,我们会提出疑问。难道这种情况下,泛型的信息被保留到了字节码里面去了吗?如果你对一个问题有疑问那就去亲自试一下。

第一:定义一个非泛型类,然后查看其生成的字节码文件

public class FruitsContainer{public Number mNum;
}

我用 Bytecode Viewer 这个工具来查看生成的字节码文件FruitsContainer.class,如下图所示:第一部分是源代码,第二部分是从class文件中反编译出的代码,第三部分是字节码文件内容。

我们其实要重点关注的是字节码的那部分,看与其泛型版本的异同

第二:定义一个泛型类,然后查看其生成的字节码文件

public class Fruit {
}public class FruitsContainer<T extends Fruit>{public T t;
}

如下图所示:

我们先看最右侧的原始的字节码文件有何异同。可以看到,在第三行多了&<T:Ladvanced/Fruit:>,下面还有几处带<>的地方,这些就是被保留了的泛型信息。其实从第二部分反编译后的源码更容易看出,多了很多Signature.

可见,在这种情况下,泛型的信息被保留到了字节码文件中。

结论

Java泛型信息是否擦除有如下两种情况: 1. 声明侧泛型信息保留 例如声明泛型接口,泛型类,泛型方法时的泛型信息 2. 使用侧泛型信息擦除 例如方法的局部变量等。

获取并使用泛型

下面举一个如何获取并使用泛型信息的例子,接着上面的代码,我们定义如下两个类

public class Apple extends Fruit {
}public class AppleContainer extends FruitsContainer<Apple> {public AppleContainer appleContainer;
}

其中AppleContainer继承至FruitsContainer 泛型类.我们可以通过如下代码获得AppleContainer带泛型信息的父类FruitsContainer<advanced.Apple>

private static void genericTest(){Type superType=(AppleContainer.class.getGenericSuperclass());System.out.println(superType);if (superType instanceof ParameterizedType){ParameterizedType paramType=(ParameterizedType)superType;System.out.println(Arrays.toString(paramType.getActualTypeArguments()));}}

输出:

advanced.FruitsContainer<advanced.Apple>
[class advanced.Apple]

可见,我们是有能力在运行时获取声明类型的泛型信息的。

使用场景

那么在运行时获取泛型信息有什么用呢?我现在遇到过两个使用场景。 1. 对泛型类型进行校验 在Retrofit2这个网络请求库中大量使用,例如其要求定义的方法返回类型必须是泛型的,例如Call<T>,而不能是Call。它还会校验T是否为合法的类型. 2. 序列化和反序列化 例如使用Gson时,我们需要将·json·转换为Java对象,假设这个对象是带泛型信息,就得告诉框架要转换的那个java对象的泛型类型。

List<CommentBean>= fromJson("json...",new TypeToken<ArrayList<CommentBean>>(){}.getType());

上面那段反序列化的代码就是要告诉Gson框架泛型参数为ArrayList<CommentBean>>。这里面又有一个很有趣的点,就是 new TypeToken<ArrayList<CommentBean>>(){} 而不是 new TypeToken<ArrayList<CommentBean>>() 加上一对{}就表示是匿名类,那样通过编译器编译后才能正确获取到其泛型信息。

例如我们有如下代码,那么大家想一想,输出是什么呢?

System.out.println(new FruitsContainer<Apple>().getClass().getGenericSuperclass());System.out.println(new FruitsContainer<Apple>(){}.getClass().getGenericSuperclass());

输出:

class java.lang.Object
advanced.FruitsContainer<advanced.Apple>

留个思考题,为什么会这样?有兴趣的可以在评论区讨论,其实上面已经给出了提示。

总结

如果你仍然在做技术,那就努力提高你的技术,虽然真理无穷。但怕什么真理无穷,进一寸有一寸的欢喜!

最后,求关注,求点赞!有任何疑问可以评论留言,我会尽力回复的。

java 获取泛型_聊聊Java泛型擦除那些事相关推荐

  1. java final 内存_聊聊 Java 内存模型

    原标题:聊聊 Java 内存模型 *作者:青芒@有赞 本文目录 Java内存模型 重排序 内存屏障 volatitle的内存语义 final的内存语义 一.Java内存模型 硬件处理 电脑硬件,我们知 ...

  2. java获取天气预报_使用java获取未来7天天气信息,可用于android

    环境:eclipsse, jdk1.6, 没有使用第三方的包,都是JDK有的. 项目结构如下: 1.获取天气预报的类 WeatherUtil.java [java] package com.siqi. ...

  3. java 获取文件大小_阿里Java后端开发面经,面试官都替我感到绝望

    点关注,不迷路:持续更新Java相关技术及资讯!!! 内容源于群友投稿!记录一次阿里Java后端开发面经,分享给大家,感谢支持! 前言 秋招面试的第一家公司,也是第一次面试,真的超级紧张,从自我介绍到 ...

  4. java解决异常_聊聊Java中的异常及处理

    在编程中异常报错是不可避免的.特别是在学习某个语言初期,看到异常报错就抓耳挠腮,常常开玩笑说编程1分钟,改bug1小时.今天就让我们来看看什么是异常和怎么合理的处理异常吧! 异常与error介绍 下面 ...

  5. java获取队列长度_关注Java线程池的任务队列长度

    关注Java线程池的任务队列长度 Java 5.0 就开始自带线程池的实现,其中固定大小的线程池,对普通使用还是很好用的.就是 Executors.newFixedThreadPool ,指需要指定一 ...

  6. java 获取邮编_基于JAVA的根据地名查邮编api调用代码实例

    代码描述:基于JAVA的根据地名查邮编api调用代码实例 接口地址:http://www.juhe.cn/docs/api/id/66 1.[代码][Java]代码 import java.io.Bu ...

  7. java获取公钥_使用java中的Bouncy Castle从CSR文件中获取公钥CSR

    ASCII格式的公钥这取决于你所说的"ASCII格式"是什么.但是,一般来说,你可以做这样的事情: // Read the CSR FileReader fileReader = ...

  8. java object转泛型_为什么Java的泛型要用擦除实现

    在 Java 中的 泛型 ,常常被称之为 伪泛型 ,究其原因是因为在实际代码的运行中,将实际类型参数的信息擦除掉了 (Type Erasure) .那是什么原因导致了 Java 做出这种妥协的呢?下面 ...

  9. android 集成同一interface不同泛型_【Java视频教程】day30-泛型??

    泛型 泛型的引入 需求:打印集合中所有字符串的长度: 结论: 再使用集合时,因为集合中可以保存不同类型的数据,保存进入集合后不管什么类型的数据都会自动向上转型为Object类型, 所以实际使用时一般需 ...

最新文章

  1. myeclipse乱码
  2. 【Flutter】Image 组件 ( 配置本地 gif 图片资源 | 本地资源加载 placeholder )
  3. Mysql内置优化工具show profiles
  4. Deepin中设置文件或文件夹权限
  5. Windows上编译github源码方式运行Node-RED,以及离线迁移安装Node-RED
  6. 实验吧——SQL注入 Write up(一)
  7. 三十三、数据仓库的概述
  8. SpringCloud Ribbon负载均衡介绍及使用
  9. Deformable Shape Completion with Graph Convolutional Autoencoders
  10. Linux DNS服务详解——DNS基础知识
  11. enum class
  12. 21个实用便利的PHP代码
  13. 自然电位测井 基本原理、测量方法、曲线规律、曲线用途
  14. Android半透明+RGB颜色代码大全
  15. 产品部和业务部门是什么关系
  16. net-java-php-python-篮球新闻网站计算机毕业设计程序
  17. 怎么用java调用快递接口查询和寄件等功能java-demo
  18. Qt官方示例:Fridge Magnets Example(冰箱贴)
  19. 牛客网:乘积为正数的最长连续子数组
  20. igh ethercat主站文档(中文翻译上)

热门文章

  1. 程序员躲不掉的“中年危机”
  2. 嵌入式智能国际大会图文直播:探索人工智能的前世今生
  3. 一位程序员 8 年的物联网奋斗史
  4. 与吴恩达并肩战斗,她是颜值爆表的 AI 科学家!
  5. 当我们在谈论单测时我们在谈论什么
  6. 未来 10 年的科技圈,将会因这 10 项技术而颠覆!
  7. 滴滴又出事!33项问题被查,程维再次致歉
  8. MySQL 8.0 正式版发布,比 MySQL 5.7 快 2 倍!
  9. AMD 芯片被曝大量安全漏洞,Linux 之父怒评!
  10. 如何才能通过一线互联网公司面试?全网疯传