对于gson我们很熟悉了
常用的复杂对象的gson解析方式:
Type listType = new TypeToken<ArrayList> () {}.getType();
但这一次我需要实现对
Type listType = new TypeToken<ArrayList> () {}.getType();的解析,
当我传入泛型T=Port IP时,返回结果并非我预期的ArrayList,而是ArrayList,为啥呢?
一开始我怀疑时kotlin泛型的问题,用java写了一遍,结果同样
然后我怀疑是gson的问题,搜了一下果然有人跟我一样。
原因:
Gson解析时TypeToken的泛型参数只能在使用时传入确切的类型才能获取正确的Type, 这是TypeToken设计成抽象类的巧妙之处和原因,也是此次问题出现的原因。
(改为只有protected构造方法的普通类原理一样). 一旦将TypeToken改成普通类, 根据上面的分析, 一切类型信息都被擦除, Gson解析将得不到预期的类型,但是强大的gson组件帮了我们一把,把结果解析成LinkedTreeMap了。
那怎么办呢?
写抽象类搞定 TypeToken<ArrayList> 这段
public abstract class GsonType<T> {
Type type;
public GsonType(){
Type genericSuperclass = getClass().getGenericSuperclass();
if(genericSuperclass instanceof Class){
throw new RuntimeException("Missing type parameter.");
}
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
Type[] typeArguments = parameterizedType.getActualTypeArguments();
type = typeArguments[0];
}
public Type getType() {
return type;
}
}
//使用
//给type赋值
saveDataHelper.gsonType = object: GsonType<ArrayList<PortIP>>(){}
//解析
mDatas =saveDataToJsonHelper.mGson.fromJson(jsonStr, gsonType.getType()) ;
上述代码是参考的,原文解析
然后我看了看TypeToken的源码发现
this.type = getSuperclassTypeParameter(getClass());
/**
* 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]);
}
嗯其实是一样的,GsonType是对TypeToken的核心提取版
简略说一下我的心得体会:
1)TypeToken要求具体类型,而需求是传递抽象类,因此只能仿照写一个
2)采用抽象类定义,是为了使用匿名类实现时,使匿名类携带具体的类
3)getClass().getSuperclass()获取的是当前对象所属的类型的父类型
4) getActualTypeArguments()返回的是确切的类型参数数组, 此处GsonType只有一个类型参数, 返回的是ArrayList[PortIP.class].