Gson源码分析(贰) 类型获取和代码规范

我们使用Gson的时候基本是需要先定义一个数据模型,然后通过一个String流转化为我们OO的对象。那么对于一个框架来说,如何去获得用户想要的数据类型呢?并且我们又要如何通过这种既定的类型来构造出我们需要的对象?或许你的第一反应就是传递一个Clazz进去,然后通过反射的方法来获得我们的实际对象。跟着这个想法我们来实验一下:

public <T>T createObject(Class<T> clazz) {try {return (T)clazz.newInstance();} catch (Exception e) {return null;}}这样看起来似乎就很和谐,似乎已经够了,但是我们需要考虑的问题是我们还有一个集合类型,而这些集合类型对其中的元素使用泛型来限制,比如List<Obj>。那么问题就出现了,我们如何来调用List<Obj>.class?当然你深入一点想还能想到非常多的问题。我们来分析一下Gson的源码就知道Gson如何解决我们的第一个问题也是Gson在处理的时候要解决的问题—获取类型问题。实际上我们在分析源码的时候能很好的了解Gson开发人员的代码设计规范和理念,包括我们一会儿分析的类型处理。我们的重点常常偏离了Gson本身设计的功能目的,更多的实阐述,Gson在Java反射方面的应用。

我们知道对于普通类型的对象我们可以通过Gson提供的fromJson(String,Clazz)来直接生成我们OO层的对象:

public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {Object object = fromJson(json, (Type) classOfT);return Primitives.wrap(classOfT).cast(object); }这个方法我们用之前的思路似乎已经可以很好的解决问题,我们更希望的是用更为通用的方法来有效的解决泛型的问题。在Gson里面通过TypeToken类来解决我们的问题。同时Gson也用了很取巧的方式,来让用户更好的使用泛型。public class TypeToken<T> { final Class<? super T> rawType; final Type type; final int hashCode; @SuppressWarnings("unchecked") protected TypeToken() {this.type = getSuperclassTypeParameter(getClass());this.rawType = (Class<? super T>) $Gson$Types.getRawType(type);this.hashCode = type.hashCode(); }}我们看到,TypeToken支持了泛型的写法,目的是为了让使用者更好的调用。另外我们还可以看到TypeToken很取巧的部分,它为了迫使用户有意识的使用泛式写法,将构造器定义成了protected。这样,使用者不得不通过继承的方式,或者匿名类的方式来生成一个TypeToken对象。或许你很奇怪里面的一个类名定义:$Gson$Type.实际上,在Gson里面,所有的静态功能方法都会包含在同一个类中,这个类就是以$打头,或者这么说,所有以$打头的类都是一些静态方法的集合。/** * Returns the type from super class's type parameter in {@link $Gson$Types#canonicalize * canonical form}. */ static Type getSuperclassTypeParameter(Class<?> subclass) {Type superclass = subclass.getGenericSuperclass();if (superclass instanceof Class) {throw new RuntimeException("Missing type parameter.");}ParameterizedType parameterized = (ParameterizedType) superclass;return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]); }由于外部不得不采用匿名类的方式来生成TypeToken<?>,因此,你需要获得的泛型类型包含在你的superclass信息中。假如你的匿名类不包含任何的泛式类型,就会抛出一个"Miss type paramter"异常。我们不妨简单定义一个泛式类型来看一下调用getGenericSuperClazz后得到的东西:public class MySub extends MyGson<List<String>> {}我们执行一个getGenericSuperclass()操作,得到的是一个ParameterizedType 类型的Type。而这个Type包含有你几乎关心的所有类型元数据。包括它的拥有者,它的类名,他的泛型参数。

我们之前提到过任何以$打头的都是Gson提供的静态工具类。canonicalize 方法的目的是规范我们在泛型内的参数,并且转化成Gson自己既定的数据结构。那什么叫做规范呢?我们还是使用MySub类作为例子,我们得到的superclass的type是MyGson<List<String>>,由于它只有一个泛型参数,因此parameterized.getActualTypeArguments()[0]方法调用之后的结果就是List<String>这个Type。或许你很纳闷为什么我们需要一个Type类呢?实际上我们知道类是对象是类的个体,个体的集合可以抽象成为类,那么类型如果作为个体,描述类型的类就是Type。或许你还是一头雾水,不要紧,这并不影响我们对Gson源码的分析,我们跟进到$Gson$Type来看一下它如何结构化我们的类型。

public static Type canonicalize(Type type) {if (type instanceof Class) {Class<?> c = (Class<?>) type;return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;} else if (type instanceof ParameterizedType) {ParameterizedType p = (ParameterizedType) type;return new ParameterizedTypeImpl(p.getOwnerType(),p.getRawType(), p.getActualTypeArguments());} else if (type instanceof GenericArrayType) {GenericArrayType g = (GenericArrayType) type;return new GenericArrayTypeImpl(g.getGenericComponentType());} else if (type instanceof WildcardType) {WildcardType w = (WildcardType) type;return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());} else {// type is either serializable as-is or unsupportedreturn type;} }关于Type类的继承树的()。

从上面的代码,我们可以比较清楚的看出,Gson对每一Type都转化为自己的数据结构,原因很简单,因为你无法很直接的获取到jdk内部的接口实现。我们打开一个Type实现类看看:

public ParameterizedTypeImpl(Type ownerType, Type rawType, Type… typeArguments) {// require an owner type if the raw type needs itif (rawType instanceof Class<?>) {Class<?> rawTypeAsClass = (Class<?>) rawType;checkArgument(ownerType != null || rawTypeAsClass.getEnclosingClass() == null);checkArgument(ownerType == null || rawTypeAsClass.getEnclosingClass() != null);}this.ownerType = ownerType == null ? null : canonicalize(ownerType);this.rawType = canonicalize(rawType);this.typeArguments = typeArguments.clone();for (int t = 0; t < this.typeArguments.length; t++) {checkNotNull(this.typeArguments[t]);checkNotPrimitive(this.typeArguments[t]);this.typeArguments[t] = canonicalize(this.typeArguments[t]);}}

实际上,Gson相当于clone了一便JDK内部的数据类型,差别就在于,这个数据结构可以由Gson框架自己控制。此外,我们可以很清楚的看出Gson内部,采用递归的方式来创建内部数据类型。举个例子来说明:

“人无完人金无足赤”,只要是人就不会是完美的,

Gson源码分析(贰) 类型获取和代码规范

相关文章:

你感兴趣的文章:

标签云: