总结借助TypeToken原理的分析,我们可以加强对泛型擦除的理解,从而知道何时以及如何获取泛型。泛型擦除众所周知,Java泛型只在编译时有效,而泛型类型会在运行时被擦除,即List和List在运行时实际上是List类型。为什么选择这种实现机制?不能抹掉吗?Java诞生十年后,我想实现一个类似于C++模板的概念,即泛型。Java的类库是Java生态系统中非常宝贵的资产,必须保证向后兼容(即现有代码和类文件仍然合法)和迁移兼容性(泛化代码和非泛化代码可以相互调用)基于基于这两个背景和考虑,Java设计者采用了“类型擦除”的折中实现。同时,还有这样一个“坑”机制,使得我们在运行时无法随意获取泛型参数的具体类型。用过TypeToken和用过Gson的同学都知道,在反序列化的时候需要定义一个TypeToken类型,像这样privateTypetype=newTypeToken>>(){}.getType();//调用fromJsonmethod传类型的时候,如果type的类型和json一致,就可以反序列化。gson.fromJson(json,类型);三问1、为什么要用TypeToken来定义反序列化的类型?上面说了,如果直接传List>的类型,但是因为泛型在运行时被抹掉了,得到的其实是List,那么后面的Gson就不知道要转换了为Map类型,Gson默认会转为LinkedTreeMap类型。2、为什么要有大括号{}?花括号是本质。大家都知道在Java语法中,在这个上下文中,{}用来定义一个匿名类,它继承了TypeToken类,它是TypeToken的子类。3、为什么要使用子类来获取泛型类型?这是TypeToken能够获取泛型类型的关键,是一个巧妙的方法。思路是这样的,既然List中的泛型会被抹掉,那么如果我用一个子类SubListextendsList,在JVM内部会不会使用父类泛型呢?类型保存类型呢?我的子类需要继承的父类的泛型已经确定了。果然JVM保存了这部分信息,保存在子类的Class信息中。具体看:https://stackoverflow.com/questions/937933/where-are-generic-types-stored-in-java-class-files那我们怎么获取这部分信息呢?还好,Java有提供API出来:TypemySuperClass=foo.getClass().getGenericSuperclass();Typetype=((ParameterizedType)mySuperClass).getActualTypeArguments()[0];System.out.println(type);分析一下这一段代码,Class类的getGenericSuperClass()方法的说明是:返回代表thisClass所代表的实体(类、接口、原始类型或void)的直接超类的Type。如果超类是参数化类型,则返回的Type对象必须准确反映源代码中使用的实际类型参数。如果之前未创建表示超类的参数化类型,则创建它。有关参数化类型创建过程的语义,请参阅ParameterizedType的声明。如果thisClass表示Object类、接口、基本类型或void,则返回null。如果这个对象代表一个数组类然后返回表示Object类的Class对象简单来说,对于有泛型的类,返回一个ParameterizedType对象,对于Object、接口、原始类型,返回null,对于数组类,Object.classParameterizedType表示有是泛型参数类型的Java类型。JDK1.5引入泛型后,Java中所有的Class都实现了Type接口,ParameterizedType继承了Type接口。所有包含泛型的Class类都将实现此接口。自己调试它以了解它返回的内容。原理的核心方法就是刚才说的两句话,剩下的就很简单了。再来看看TypeToken的getType方法publicfinalTypegetType(){//直接返回类型returntype;}看看type的初始化//注意这里使用了protected关键字,限制了只能子类访问protectedTypeToken(){this.type=getSuperclassTypeParameter(getClass());this.rawType=(Class)$Gson$Types.getRawType(type);this.hashCode=type.hashCode();}//getSuperclassTypeParameter方法//这几句是上面提到staticTypegetSuperclassTypeParameter(Class>subclass){Typesuperclass=subclass.getGenericSuperclass();if(superclassinstanceofClass){thrownewRuntimeException("Missingtypeparameter.");}ParameterizedTypeparameterized=(ParameterizedType)superclass;//注意这里返回的是Gsoncustom是的,$Gson$Types等里面定义的TypeImpl,都是继承Type的。return$Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);}了解了原理之后,相信大家都知道如何获取泛型了。
掌握Java-TypeToken和泛型擦除原理相关文章