java 获取泛型_聊聊Java泛型擦除那些事
>版权申明】非商业目的注明出处可自由转载
博文地址: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泛型擦除那些事相关推荐
- java final 内存_聊聊 Java 内存模型
原标题:聊聊 Java 内存模型 *作者:青芒@有赞 本文目录 Java内存模型 重排序 内存屏障 volatitle的内存语义 final的内存语义 一.Java内存模型 硬件处理 电脑硬件,我们知 ...
- java获取天气预报_使用java获取未来7天天气信息,可用于android
环境:eclipsse, jdk1.6, 没有使用第三方的包,都是JDK有的. 项目结构如下: 1.获取天气预报的类 WeatherUtil.java [java] package com.siqi. ...
- java 获取文件大小_阿里Java后端开发面经,面试官都替我感到绝望
点关注,不迷路:持续更新Java相关技术及资讯!!! 内容源于群友投稿!记录一次阿里Java后端开发面经,分享给大家,感谢支持! 前言 秋招面试的第一家公司,也是第一次面试,真的超级紧张,从自我介绍到 ...
- java解决异常_聊聊Java中的异常及处理
在编程中异常报错是不可避免的.特别是在学习某个语言初期,看到异常报错就抓耳挠腮,常常开玩笑说编程1分钟,改bug1小时.今天就让我们来看看什么是异常和怎么合理的处理异常吧! 异常与error介绍 下面 ...
- java获取队列长度_关注Java线程池的任务队列长度
关注Java线程池的任务队列长度 Java 5.0 就开始自带线程池的实现,其中固定大小的线程池,对普通使用还是很好用的.就是 Executors.newFixedThreadPool ,指需要指定一 ...
- java 获取邮编_基于JAVA的根据地名查邮编api调用代码实例
代码描述:基于JAVA的根据地名查邮编api调用代码实例 接口地址:http://www.juhe.cn/docs/api/id/66 1.[代码][Java]代码 import java.io.Bu ...
- java获取公钥_使用java中的Bouncy Castle从CSR文件中获取公钥CSR
ASCII格式的公钥这取决于你所说的"ASCII格式"是什么.但是,一般来说,你可以做这样的事情: // Read the CSR FileReader fileReader = ...
- java object转泛型_为什么Java的泛型要用擦除实现
在 Java 中的 泛型 ,常常被称之为 伪泛型 ,究其原因是因为在实际代码的运行中,将实际类型参数的信息擦除掉了 (Type Erasure) .那是什么原因导致了 Java 做出这种妥协的呢?下面 ...
- android 集成同一interface不同泛型_【Java视频教程】day30-泛型??
泛型 泛型的引入 需求:打印集合中所有字符串的长度: 结论: 再使用集合时,因为集合中可以保存不同类型的数据,保存进入集合后不管什么类型的数据都会自动向上转型为Object类型, 所以实际使用时一般需 ...
最新文章
- myeclipse乱码
- 【Flutter】Image 组件 ( 配置本地 gif 图片资源 | 本地资源加载 placeholder )
- Mysql内置优化工具show profiles
- Deepin中设置文件或文件夹权限
- Windows上编译github源码方式运行Node-RED,以及离线迁移安装Node-RED
- 实验吧——SQL注入 Write up(一)
- 三十三、数据仓库的概述
- SpringCloud Ribbon负载均衡介绍及使用
- Deformable Shape Completion with Graph Convolutional Autoencoders
- Linux DNS服务详解——DNS基础知识
- enum class
- 21个实用便利的PHP代码
- 自然电位测井 基本原理、测量方法、曲线规律、曲线用途
- Android半透明+RGB颜色代码大全
- 产品部和业务部门是什么关系
- net-java-php-python-篮球新闻网站计算机毕业设计程序
- 怎么用java调用快递接口查询和寄件等功能java-demo
- Qt官方示例:Fridge Magnets Example(冰箱贴)
- 牛客网:乘积为正数的最长连续子数组
- igh ethercat主站文档(中文翻译上)