码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2015-03-16 21:21:49      阅读:200      评论:0      收藏:0      [点我收藏+]

标签:设计模式   开源项目   gson   递归   json   

我们使用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构建出自己的一个类型树,基本就完成了类型的采取。






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

标签:设计模式   开源项目   gson   递归   json   

原文地址:http://blog.csdn.net/hello__zero/article/details/44201033

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!