笔者有这么一个需求,根据传入的泛型,将JSON字符串使用GSON解析为指定的泛型。
像这样——
HttpService.Builder("http://192.168.0.128:9008/external/login.htm")
.addParams("file", File(FileUtils.sdCardPath + File.separatorChar + "test.apk"))
.addParams("file2", File(FileUtils.sdCardPath + File.separatorChar + "test.apk"))
.addParams("userName", "151*****066")
.addParams("passWord", "888888")
.uploadingWithJava(object : OnUploadingListener() {
override fun onStart() {
super.onStart()
}
override fun onLoading(key: String, bytesRead: Long, totalCount: Long, isDone: Boolean) {
sampleTv13.text = String.format("上传文件[%s]:%d/%d", key, bytesRead, totalCount)
Log2.d(String.format("正在上传[%s]:%s/%s", key, bytesRead.toString(), totalCount.toString()))
if ("file" == key && isDone) Log2.e("file上传完成")
else if ("file2" == key && isDone) Log2.e("file2上传完成")
}
override fun onResponse(value: TestBean?) {
sampleTv13.text = String.format("上传完成:%s", value)
// Log2.e("上传成功:$value")
}
override fun onFailure(e: IOException, isNetworkError: Boolean) {
Log2.e("上传错误")
}
override fun onEnd() {
super.onEnd()
}
})
TestBean是我请求网络成功后,需要将JSON解析为TestBean。使用过GSON的朋友都知道,解析需要传入Class,这样GSON才能通过反射将JSON解析出来。而泛型有类型擦除的特性,无法获取到Class。
在java中,泛型无法获取到类型。像这样——
public T test(ClassCallback classCallback) {
T.getClass()//无法获取T的class
}
而在Kotlin可以做到,通过内联函数解决,像这样——
inline fun test(json :String) {
GsonUtils.parseFromJson(json,T::class.java)//可以直接获取到Class
}
如此对比,不得不说Kotlin大法好!!
但是这样也不是尽善尽美,就目前来看,大部分的项目都是Java与Kotlin共存的,而Kotlin的内联函数,Java无法调用、无法调用、无法调用!!
所以,本着尽善尽美的学习态度,笔者再继续找到了解决方法。
private fun getSuperClassGenericType(clz: Class<*>): Class<*> {
val type = clz.genericSuperclass as? ParameterizedType ?: return Any::class.java
val params = type.actualTypeArguments
return if (params[0] is ParameterizedType) {
/*
((params[0] as ParameterizedType).actualTypeArguments[0] as Class<*>).canonicalName*//*TestBean T的类型*//*
((params[0] as ParameterizedType).rawType as Class<*>).canonicalName/*TestBean TestBean的类型*/
*/
(params[0] as ParameterizedType).rawType as Class<*>
} else
try {
params[0] as Class<*>
} catch (O_O: Exception) {
Any::class.java
}
}
通过反射直接获取Class中的泛型,并且做了兼容,支持TestBean以及TestBean
下面是完整的思路。
1、为了方便使用,抽象方法。
/**
* 泛型Class获取
* 解决泛型擦除的问题
* @from https://blog.csdn.net/Fy993912_chris/article/details/84765483
*/
abstract class ClassCallback {
val classReal: Class
get() = getSuperClassGenericType(javaClass) as Class
private fun getSuperClassGenericType(clz: Class<*>): Class<*> {
val type = clz.genericSuperclass as? ParameterizedType ?: return Any::class.java
val params = type.actualTypeArguments
return if (params[0] is ParameterizedType) {
/*
((params[0] as ParameterizedType).actualTypeArguments[0] as Class<*>).canonicalName*//*TestBean T的类型*//*
((params[0] as ParameterizedType).rawType as Class<*>).canonicalName/*TestBean TestBean的类型*/
*/
(params[0] as ParameterizedType).rawType as Class<*>
} else
try {
params[0] as Class<*>
} catch (O_O: Exception) {
Any::class.java
}
}
}
2、需要使用时,继承该接口
/**
* 上传监听
* @param T 泛型,解析为指定的类型
* @from https://blog.csdn.net/Fy993912_chris/article/details/84765483
*/
abstract class OnUploadingListener : OnProgressCallBack, ClassCallback() {
open fun onStart() {}
abstract fun onFailure(e: IOException, isNetworkError: Boolean)
abstract fun onResponse(value: T?)
open fun onEnd() {}
}
3、在使用的地方调用该方法
/**
* 兼容java
* 上传文件
*/
fun uploadingWithJava(builder: Builder, onUploadingListener: OnUploadingListener) {
。
。
。
//网络请求后,已接收到json
fun onRespone(json:String){
GsonUtils.parseFromJson(data, onUploadingListener.classReal)//在这里解析,调用classReal
}
}
但是在Kotlin中如何实现呢?也是相当简单的,只需要将接收的方法改为内联函数即可。像这样——
inline fun uploadingWithKotlin(builder: Builder, onUploadingListener: OnUploadingListener) {
。
。
。
//网络请求后,已接收到json
fun onRespone(json:String){
GsonUtils.parseFromJson(data, T::class.java)//在这里解析,直接可获取到Class
}
}
至此,就实现了将JSON直接解析为指定泛型了,而不需要在得到JSON后,然后再重复解析。方便了许多。
参考链接:https://blog.csdn.net/changsa65/article/details/78790881