Android 获取泛型实际类型

笔者有这么一个需求,根据传入的泛型,将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,如果获取失败,将返回Object.class。所以这个需求就可以实现了。

下面是完整的思路。

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

 

你可能感兴趣的:(Android片段,Kotlin,开发框架,QuickAndroid,基础组件)