情景引入:
在使用GSON解析一段JSON数组时,需要借助TypeToken将期望解析成的数据类型传入到fromJson()方法中,如下:
List<Person> people = gson.fromJson(jsonData, new TypeToken<List<Person>>(){}.getType());
————出自《第一行代码》
假设一段JSON格式的数据如下:
[{"name":"Tom","age":"10"},
{"name":"Lucy","age":"11"},
{"name":"Lily","age":"11"}]
那么,new TypeToken<List<Person>>(){}.getType()
是怎么获取到泛型参数类型的呢?
解析:
首先,new TypeToken<List<Person>>(){}
是一个匿名内部类,其等价MyTypeToken<List<Person>> extends TypeToken(){}
,但是{}里是空的,既然什么都没有改变,为什么还要这么用呢?下面看源码
进一步,TypeToken源码如下:
public class TypeToken<T> {
final Class<? super T> rawType;
final Type type;
final int hashCode;
//这里的空参构造方法权限修饰符是protected,那木只有其子类可访问,预示着要使用子类构造。
protected TypeToken() {
this.type = getSuperclassTypeParameter(this.getClass());//这里传入的子类,后面2行不用看
this.rawType = Types.getRawType(this.type);
this.hashCode = this.type.hashCode();
}
...
static Type getSuperclassTypeParameter(Class<?> subclass) {
Type superclass = subclass.getGenericSuperclass();//获取到子类的父类Type
if(superclass instanceof Class) {
throw new RuntimeException("Missing type parameter.");
} else {
//将Type类型向下转型为参数化类型ParameterizedType
ParameterizedType parameterized = (ParameterizedType)superclass;
//这里getActualTypeArguments()返回的是一个数组,由于只有一个泛型参数,直接[0]。
return Types.canonicalize(parameterized.getActualTypeArguments()[0]);
}
}
若是还不是太明白,那就是Java这块的东西还不熟。上面的原码部分牵涉到Java的知识点:
Type:
其是一个接口java.lang.reflect.Type,主要有5类:
raw types:一般类型,例如:String,Collections ,Math,Number…
parameterized types : 参数化类型,例如:List<String>
集合中常用…
array types : 数组类型
type variables :类型变量,不确定其类型,例如List<? extends Person>
primitive types : 基本类型,int,float…
详细参见:http://blog.csdn.net/kaka123ac/article/details/4470813
getSuperClass() 与 getGenericSuperclass()区别:
前者,返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class(由于编译擦除,没有显示泛型参数:在运行期间,泛型参数类型一律为Object类型)。
后者,返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type(包含泛型参数)。
详细参见: http://www.cnblogs.com/maokun/p/6773203.html
GSON源码中Types.canonicalize方法,将Java 中的Type实现,转化为自己内部的数据实现,想要继续探讨,可以去看源码或者参见:https://my.oschina.net/u/874727/blog/750473
最后,我们来实践一下,实现Java中如何获取参数类型,TypeToken内部就是Java实现,然后转换。直接上代码:
public class TypeToken1<String> {
public TypeToken1() {
}
//为了测试,这个类什么都不干,手动去获取参数类型String
}
----------
public class TypeTokenTest {
public static void main(String[] args){
Type mySuperClass = new TypeToken1<String>(){}.getClass().getGenericSuperclass();
Type type = ((ParameterizedType) mySuperClass).getActualTypeArguments()[0];
System.out.println("获得的泛型参数:"+type);
}
}
运行结果:
获得的泛型参数:class java.lang.String
Process finished with exit code 0