S
吧你的目的是获取S
里面的data
,这很关键
class S<T>(
val data: T
//.....省略其他成员变量
)
可以看成网络包,不解释了
函数声明如下
public <T> T fromJson(String json, Type typeOfT)
public <T> T fromJson(Reader json, Class<T> classOfT)
这里只谈第一个,假如现在要将一个S
的Json
转成对应的S
(这里为了方便就用的String
),那么正常来说需要如下的代码
val type = object: TypeToken<S<String>>(){}.type
val s = Gson().fromJson(sJson, type)
这就有一个问题,也不算是问题。就是你使用的S
具有泛型参数,但是你json
转obj
的时候还得去写TypeToken....
一堆代码,如果要转的次数多就很烦,很多样板代码。
所以似乎可以这样
fun <T> jsonToT(sJson: String){
val type = Object: TypeToken<S<T>>(){}.type
val s = Gson().fromJson(sJson,type)
}
很可惜,不可以这样,因为在编译期Java对T
进行了类型擦除,于是乎当你的代码执行到这里时,现在这个type
的name
就是object
。然后Gson
将抛出异常 linkTreeMap cannot be cast to String(因为我这用的String)。
T
传入函数的编译期类型擦除在kotlin中,使用如下代码可以解决;Java中是传入Class
,不说Java了
inline fun <reified T> jsonToT(sJson:String){
}
下面用上面这个函数框架解决异常 linkTreeMap cannot be cast to String
data
首先
这时,T
的类型会被传入这个函数
inline fun <reified T> jsonToT(sJson:String): T{
val typeS = object : TypeToken<S<T>>(){}.type
val s = Gson().fromJson(sJson, typeS)
return s.data
}
这时你以为稳了,错!当S
传入TypeToken
这个工具类时,由于TypeToken
里面的函数也是带有类型参数的函数,又进行了类型擦除,完蛋,T
又成了Object
,又是同样的异常。
所以,那我把TypeToken
里面的函数改成inline fun
不就行了,事实上可以,但是我,看不懂TypeToken
…所以我选择了改Gson().fromJson()
思路是这样,先看代码
inline fun <reified T> jsonToT(sJson:String): T{
val typeS = object : TypeToken<S<T>>(){}.type
val typeT = object : TypeToken<T>(){}.type
val s = Gson().fromJson(sJson, typeS)
val dataJson = Gson().toJson(s.data)
val data = Gson().fromJson(dataJson, typeT)
return data
}
由于T
的类型在这里没有被擦除,所以typeT
现在是String
的类型。先将s.data
转Json
,然后再使用typeT
,将dataJson
转成String
,大功告成。哈哈错!注意了,Gson().fromJson()也是一个带泛型的函数,上面我提到了的,意味着在Gson().fronJson()里面的你的typeT
,又变成了Object
,于是异常又来了 linkTreeMap cannot be cast to String。
离成功只差一步之遥了,那就是自己写一个fromJson()
,我给他命名fromJson2()
使他使用我们的模板,让传入的T
类型不再被擦除:
inline fun <reified T> fromJson2(json: String?, typeOfT: Type) : T?{
}
当然,不可能自己完全从头写。这里使用拓展函数,似乎是kotlin才有,我没去查了,反正这里用。
//拓展函数 Kotlin
@Throws(JsonSyntaxException::class)
inline fun <reified T> Gson.fromJson2(json: String?, typeOfT: Type) : T?{
if (json == null) {
return null
}
val reader = StringReader(json)
return fromJson(reader, typeOfT)
}
//原来的 Java
public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
if (json == null) {
return null;
}
StringReader reader = new StringReader(json);
T target = (T) fromJson(reader, typeOfT);
return target;
}
拓展函数可以和原来的Gson
完美契合,而且不用更改Gson
的任何代码。那个异常也要抛出,Gson
内部需要处理,和Java
相比只是换了个写法。好的,完成了,用Gson().fromJson2()
,去代替最后一个Gson().fromJson()
。
inline fun <reified T> jsonToT(sJson:String): T{
val typeS = object : TypeToken<S<T>>(){}.type
val typeT = object : TypeToken<T>(){}.type
val s = Gson().fromJson(sJson, typeS)
val dataJson = Gson().toJson(s.data)
val data = Gson().fromJson2(dataJson, typeT)
return data
}
这样,随便扔一个S
的Json
数据进去,只要在扔的时候指定想获取的data
的类型就可以了。说这么多,其实就是为了少些两行模板代码…还不是懒嘛