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 unsupported return type; } }关于Type类的继承树的(http://docs.oracle.com/javase/6/docs/api/java/lang/reflect/Type.html)。
从上面的代码,我们可以比较清楚的看出,Gson对每一Type都转化为自己的数据结构,原因很简单,因为你无法很直接的获取到jdk内部的接口实现。我们打开一个Type实现类看看:
public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) { // require an owner type if the raw type needs it if (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内部,采用递归的方式来创建内部数据类型。举个例子来说明:
String gsonValue = "[{name:'非子墨',age:23},{name:'非墨',age:23}]"; TypeToken typeToken = new TypeToken<User[]>(){};我们通过Gson的TypeToken获得一个User数组类型,那么按照我们之前的分析,它会调用到$Gson$Type的canonicalize方法,由于User[]是一种数据类型,因此它在canonicalize 方法中走的case就是:
if (type instanceof GenericArrayType) { GenericArrayType g = (GenericArrayType) type; returnType = new GenericArrayTypeImpl(g.getGenericComponentType()); }我们接着往下走,跟到GenericArrayTypeImpl的内部看看
public GenericArrayTypeImpl(Type componentType) { this.componentType = canonicalize(componentType); }我们看到在数组类型,采用递归的方式记录了数据类型中成员的类型数据。之所以要调用canonicalize就是要将它转化为Gson自己的数据类型。通过这种方式,Gson构建出自己的一个类型树,基本就完成了类型的采取。
。