关于Gson的TypeToken-创新互联
- 引言
- Type是什么
- 获取类型的困惑
- 自定义TypeToken
- 解决问题
- 总结
引言
Gson在Json解析中使用广泛, 常用的数据类型都可以解析, 特殊的可以自定义Adapter解析. 在解析大量具有某些相同结构的数据上,我们总想复用已有的类型, 为了复用通常可以使用继承和泛型. 比如服务端返回的json都有类似结构:
{"code":200,
"message":"success",
"data":"{...}"
}
其中data
对应的结构不定, 一种考虑是使用泛型:
public class Response{public T data;//简化数据, 省略了其他字段
}
于是在做json解析是很可能会这样使用:
String json = "{\"data\":\"data from server\"}";
Type type = new TypeToken>(){}.getType();
Responseresult = new Gson().fromJson(json, type);
这个TypeToken
是如何和类型Response
产生关系,又是怎样存储泛型信息的? 首先需要明确Type是什么.
这里的Type指java.lang.reflect.Type, 是Java中所有类型的公共高级接口, 代表了Java中的所有类型. Type体系中类型的包括:数组类型(GenericArrayType)、参数化类型(ParameterizedType)、类型变量(TypeVariable)、通配符类型(WildcardType)、原始类型(Class)、基本类型(Class), 以上这些类型都实现Type接口.
参数化类型,就是我们平常所用到的泛型List、Map;
数组类型,并不是我们工作中所使用的数组String[] 、byte[],而是带有泛型的数组,即T[] ;
通配符类型, 指的是>, extends T>等等
原始类型, 不仅仅包含我们平常所指的类,还包括枚举、数组、注解等;
基本类型, 也就是我们所说的java的基本类型,即int,float,double等
本文的重点在于参数化类型(ParameterizedType).
public interface ParameterizedType extends Type {// 返回确切的泛型参数, 如Map返回[String, Integer]
Type[] getActualTypeArguments();
//返回当前class或interface声明的类型, 如List>返回List
Type getRawType();
//返回所属类型. 如,当前类型为O.I, 则返回O. 顶级类型将返回null
Type getOwnerType();
}
获取类型的困惑对于普通的类想要获取类型简单调用.class
或者getClass()
方法即可,
ClassstringClass = String.class;
Class>stringClass2 = "hello".getClass();
但对于泛型你不能这样做,
Response.class //不能通过编译
Response.class //只能这样获取类型, 但无法知道元素的类型
那么在做json解析时我们如果确实是需要让Gson解析成Response
, 可以像上文的方式处理.
先看一段代码:
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
public abstract class MyTypeToken{private final Type type;
public MyTypeToken() {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;
}
}
MyTypeToken声明为抽象类, 使用时需要对其进行实例化, 实例化过程可以分解如下:
MyTypeToken
sToken = new MyTypeToken (){}; 相当于
class MyTypeToken$0 extends MyTypeToken
{} MyTypeToken sToken = new MyTypeToken$0(); 这样分解的目的在于明确
sToken
的类型是MyTypeToken$0(匿名的)
,父类型是MyTypeToken
而不是MyTypeToken
.getClass().getSuperclass()
获取的是当前对象所属的类型的父类型. 注意到抽象类实例化时需要给具体的泛型类, 如果没有提供则使用Object(但此时使用的已不是泛型类了, 而是原始类型, 也就是擦除泛型后的类型)代替泛型参数. 因此如果像上面那样实例化, 那么getClass().getGenericSuperclass()
得到的将是类型参数实例化后的父类型MyTypeToken
, 泛型信息保留下来了. 如果不用泛型得到的是MyTypeToken
, 是原始类型.得到泛型参数实例化后的类型,
getActualTypeArguments()
返回的是确切的类型参数数组, 此处MyTypeToken只有一个类型参数, 返回的是数组[String.class]
.
至此, 通过new MyTypeToken>() {}.getType()
得到的正是表示Response
的类型, 将该类型应用在Gson在解析Response
上将获得和TypeToken
一致的效果(当然就这么点代码功能肯定是比不上了).
探究TypeToken的目的其实为了解决以下问题而总结的.
//封装getType()操作
public class TokenUtil{public staticType getType(){return new MyTypeToken() {}.getType();
}
}
// 本意在于获取Response的类型, 但是new MyTypeToken() {}时已经实现了
// 抽象类, 相当于创建一个子类 class MyTypeToken$0extends MyTypeToken{},
// 实例化时虽然传入的是Response, 但.getGenericSuperclass()=MyTypeToken$0// 于是getType()返回的只是泛型参数类型E, 正真解析是按照Gson流程选择Map或者List
Type type = TokenUtil.>getType();
// fromJson返回Map或者List, ClassCastException!
Responseo = new Gson().fromJson(json, type);
总结Gson解析时TypeToken
的泛型参数只能使用时传入确切的类型才能获取正确的Type, 这也是TypeToken
设计成抽象类的巧妙之处和原因(改为只有protected构造方法的普通类原理一样). 一旦将TypeToken
改成普通类, 根据上面的分析, 一切类型信息都被擦除, Gson解析将得不到预期的类型.
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
新闻名称:关于Gson的TypeToken-创新互联
当前URL:http://scyanting.com/article/dpecdi.html