当前位置:网站首页>掌握 TypeToken 原理及泛型擦除
掌握 TypeToken 原理及泛型擦除
2022-08-09 02:54:00 【小鲁蛋儿】
目录
1、泛型擦除
众所周知,Java的泛型只在编译时有效,到了运行时这个泛型类型就会被擦除掉,即List<String>和List<Integer>在运行时其实都是List<Object>类型。
代码测试
@Test
public void test(){
ArrayList<String> list1 = new ArrayList<>();
ArrayList<Integer> list2 = new ArrayList<>();
System.out.println(list1.getClass());
System.out.println(list2.getClass());
}
为什么选择这种实现机制?不擦除不行么?
在Java诞生10年后,才想实现类似于C++模板的概念,即泛型。Java的类库是Java生态中非常宝贵的财富,必须保证向后兼容(即现有的代码和类文件依旧合法)和迁移兼容(泛化的代码和非泛化的代码可互相调用)基于上面这两个背景和考虑,Java设计者采取了“类型擦除”这种折中的实现方式。
同时正有这个这么“坑”的机制,令我们无法在运行期间随心所欲的获取到泛型参数的具体类型。
2、TypeToken
使用过Gson的同学都知道在反序列化时需要定义一个TypeToken类型,像这样
Type type = new TypeToken<ArrayList<Person>>() {}.getType();
List<Person> list1 = gson.fromJson(listJsonString, type);
三个问题
1、为什么要用TypeToken来定义反序列化的类型?
正如上面说的,如果直接把 ArrayList<Person>
的类型传过去,因为运行时泛型被擦除了,所以得到的其实是 ArrayList<Object>
,那么后面的Gson就不知道要转成 ArrayList<Person>
类型了。
2、为什么带有大括号{}?
这个大括号就是精髓所在。大家都知道,在Java语法中,在这个语境,{}是用来定义匿名类,这个匿名类是继承了TypeToken类,它是TypeToken的子类。
测试
@Test
public void test2(){
System.out.println(new TypeToken<ArrayList<Person>>() {}.getClass().getSuperclass());
}
结果
3、为什么要通过子类来获取泛型的类型?
这是TypeToken能够获取到泛型类型的关键,这是一个巧妙的方法。这个想法是这样子的,既然像ArrayList<Person>
这样中的泛型会被擦除掉,那么我用一个子类 SubList extends ArrayList<Person>
这样的话,在JVM内部中会不会把父类泛型的类型给保存下来呢?
我这个子类需要继承的父类的泛型都是已经确定了的呀,果然,JVM是有保存这部分信息的,它是保存在子类的Class信息中。
那么我们怎么获取这部分信息呢?还好,Java有提供API出来
public class TestMethod {
@Test
public void test1(){
Student student = new Student();
/**
* getGenericSuperclass()获得带有泛型的父类
* Type是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。
*/
Type mySuperClass = student.getClass().getGenericSuperclass();
Type type = ((ParameterizedType) mySuperClass).getActualTypeArguments()[0];
System.out.println(type);
}
}
class Student extends ArrayList<Map<Integer, Boolean>>{
}
概括来说就是对于带有泛型的class,返回一个ParameterizedType对象,对于Object、接口和原始类型返回null,对于数组class则是返回Object.class。
ParameterizedType是表示带有泛型参数的类型的Java类型,JDK1.5引入了泛型之 后,Java中所有的Class都实现了Type接口,ParameterizedType则是继承了Type接口,所有包含泛型的Class类都会实现 这个接口。
3、原理
getType方法
public final Type getType() {
return this.type;
}
type的初始化
protected的作用范围是在同一包内可被访问和继承。不同包内,子类可继承,非子类不能访问
由于是第三方 jar 包,所以只有子类才能访问。
protected TypeToken() {
this.type = getSuperclassTypeParameter(getClass());
this.rawType = (Class<? super T>) $Gson$Types.getRawType(type);
this.hashCode = type.hashCode();
}
getSuperclassTypeParameter方法
里面的代码就是上面说到的获取父类泛型参数的方法
static Type getSuperclassTypeParameter(Class<?> subclass) {
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class) {
throw new RuntimeException("Missing type parameter.");
}
ParameterizedType parameterized = (ParameterizedType) superclass;
//这里注意一下,返回的是Gson自定义的,这个类是继承Type的。
return Types.canonicalize(parameterized.getActualTypeArguments()[0]);
}
在了解原理之后,相信大家都知道怎么去获取泛型的类型了。
边栏推荐
- Financial Industry Software Testing Interview Questions (with Answers) | Getting Started Guide
- [LeetCode84双周赛] [模拟] 6174. 任务调度器 II,[贪心&数学] 6144. 将数组排序的最少替换次数
- C专家编程 第10章 再论指针 10.1 多维数组的内存布局
- win10上运行emwin
- OpenLORIS-Object Datasets
- JS 截取数组的最后几个元素
- Zabbix 5.0 监控教程(四)
- 带你做接口测试从零到第一条用例 总结
- 数学基础(四)极大似然估计、误差的高斯分布与最小二乘估计的等价性
- 【扫雷--2】
猜你喜欢
随机推荐
【机器学习】21天挑战赛学习笔记(三)
继承 Inheritance
二分搜索法和二叉搜索树
opencv学习入门
马斯克被因狗狗币被索赔2580亿美元 操纵价格牟利?狗狗币已下跌92%
如何实现有状态转化操作
多御安全浏览安卓版升级尝鲜,新增下载管理功能
(面试题)面试官为啥总是让我们手撕call、apply、bind?
The building had been registry cluster, load balancing
button click animation
Redis中SDS简单动态字符串
Processing Point Clouds
Doris从理论详解到千万级数据量场景使用
按钮点击动画
online schema change and create index
Matlab实现异构交通流
自动化测试框架总结
Building PO layered architecture of automated testing framework from 0
dice和iou
【网络教程】IPtables官方教程--学习笔记3