栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 软件开发 > 后端开发 > Java

掌握 TypeToken 原理及泛型擦除

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

掌握 TypeToken 原理及泛型擦除

目录

1、泛型擦除

为什么选择这种实现机制?不擦除不行么?

2、TypeToken

1、为什么要用TypeToken来定义反序列化的类型?

2、为什么带有大括号{}?

 3、为什么要通过子类来获取泛型的类型?

3、原理


1、泛型擦除

众所周知,Java的泛型只在编译时有效,到了运行时这个泛型类型就会被擦除掉,即List和List在运行时其实都是List类型。

代码测试

    @Test
    public void test(){
        ArrayList list1 = new ArrayList<>();
        ArrayList 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>() {}.getType();

List list1 = gson.fromJson(listJsonString, type);

三个问题

1、为什么要用TypeToken来定义反序列化的类型?

正如上面说的,如果直接把 ArrayList 的类型传过去,因为运行时泛型被擦除了,所以得到的其实是 ArrayList ,那么后面的Gson就不知道要转成 ArrayList 类型了。

2、为什么带有大括号{}?

这个大括号就是精髓所在。大家都知道,在Java语法中,在这个语境,{}是用来定义匿名类,这个匿名类是继承了TypeToken类,它是TypeToken的子类。

测试 

    @Test
    public void test2(){
        System.out.println(new TypeToken>() {}.getClass().getSuperclass());
    }

 结果

 3、为什么要通过子类来获取泛型的类型?

这是TypeToken能够获取到泛型类型的关键,这是一个巧妙的方法。这个想法是这样子的,既然像ArrayList 这样中的泛型会被擦除掉,那么我用一个子类 SubList extends ArrayList这样的话,在JVM内部中会不会把父类泛型的类型给保存下来呢?

我这个子类需要继承的父类的泛型都是已经确定了的呀,果然,JVM是有保存这部分信息的,它是保存在子类的Class信息中。

那么我们怎么获取这部分信息呢?还好,Java有提供API出来

public class TestMethod {
    @Test
    public void test1(){
        Student student = new Student();
        
        Type mySuperClass = student.getClass().getGenericSuperclass();

        Type type = ((ParameterizedType) mySuperClass).getActualTypeArguments()[0];
        System.out.println(type);
    }
}

class Student extends ArrayList>{
}

 

 概括来说就是对于带有泛型的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) $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]);
  }

在了解原理之后,相信大家都知道怎么去获取泛型的类型了。

参考文章:掌握 Java-TypeToken 原理及泛型擦除-Java知音 (javazhiyin.com)

转载请注明:文章转载自 www.wk8.com.cn
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号