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<TestBean>() {

            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> T test(ClassCallback<T> classCallback) {
    T.getClass()//无法获取T的class
}

而在Kotlin可以做到,通过内联函数解决,像这样——

inline fun <reified T> 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> T的类型*//*
            ((params[0] as ParameterizedType).rawType as Class<*>).canonicalName/*TestBean<T> TestBean的类型*/
            */
            (params[0] as ParameterizedType).rawType as Class<*>
        } else
            try {
                params[0] as Class<*>
            } catch (O_O: Exception) {
                Any::class.java
            }
    }

通过反射直接获取Class中的泛型,并且做了兼容,支持TestBean以及TestBean<String>,如果获取失败,将返回Object.class。所以这个需求就可以实现了。

下面是完整的思路。

1、为了方便使用,抽象方法。

import java.lang.Exception
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.lang.reflect.WildcardType

/**
 * 泛型Class获取
 * 解决泛型擦除的问题
 * @from https://blog.csdn.net/Fy993912_chris/article/details/84765483
 */
abstract class ClassCallback<T> {

    /**
     * 泛型Class
     * TestBean<String,Int> TestBean的Class,若无,返回Object.Class
     */
    val tClass: Class<T>
        get() = getTClass(javaClass) as Class<T>

    /**
     * 泛型中的泛型列表
     * TestBean<String,Int> String,Int的Class,若无,返回列表为空
     */
    val tTClass: ArrayList<Class<T>>
        get() = getTTClass(javaClass) as ArrayList<Class<T>>

    /**
     * 子类的泛型
     */
    val tCurrentClz: Class<T>
        get() = getTCurrentClass(javaClass) as Class<T>

    companion object {

        /**
         * 获取子类的泛型
         */
        fun getTCurrentClass(clz: Class<*>): Class<*> {
            val type = getClzType(clz)
            return try {
                type as Class<*>
            } catch (O_O: Exception) {
                Any::class.java
            }
        }


        /**
         * 获取父类泛型的类型
         * 向后查找三层
         */
        fun getTClass(clz: Class<*>): Class<*> {
            val superSuperType = getClzType(clz.superclass.superclass)
            val superType = superSuperType ?: getClzType(clz.superclass)
            val type = superType ?: getClzType(clz)
            return try {
                type as Class<*>
            } catch (O_O: Exception) {
                Any::class.java
            }
        }


        fun getClzType(clz: Class<*>): Type? {
            val type = clz.genericSuperclass as? ParameterizedType ?: return null

            val params = type.actualTypeArguments
            return when {
                params[0] is ParameterizedType ->/*泛型内还有泛型*/
                    (params[0] as ParameterizedType).rawType
                params[0] is Class<*> ->
                    params[0]
                else ->
                    null
            }
        }

        fun getTTClass(clz: Class<*>): ArrayList<Class<*>> {
            val clzArray = arrayListOf<Class<*>>()
            val type = clz.genericSuperclass as? ParameterizedType ?: return clzArray
            val params = type.actualTypeArguments
            if (params[0] is ParameterizedType) {
                when (val pType = (params[0] as ParameterizedType).actualTypeArguments[0]) {
                    is Class<*> ->
                        clzArray.add(pType)
                    is WildcardType -> {
                        if (pType.upperBounds[0] is Class<*>) {
                            clzArray.add(pType.upperBounds[0] as Class<*>)
                        }
                    }
                }
            }
            return clzArray
        }
    }
}

2、需要使用时,继承该接口

/**
 * 上传监听
 * @param T 泛型,解析为指定的类型
 * @from https://blog.csdn.net/Fy993912_chris/article/details/84765483
 */
abstract class OnUploadingListener<T> : OnProgressCallBack, ClassCallback<T>() {

    open fun onStart() {}

    abstract fun onFailure(e: IOException, isNetworkError: Boolean)
    abstract fun onResponse(value: T?)

    open fun onEnd() {}
}

3、在使用的地方调用该方法

/**
     * 兼容java
     * 上传文件
     */
    fun <T> uploadingWithJava(builder: Builder, onUploadingListener: OnUploadingListener<T>) {
        。
        。
        。
        //网络请求后,已接收到json
        fun onRespone(json:String){
            GsonUtils.parseFromJson(data, onUploadingListener.classReal)//在这里解析,调用classReal
        }
    }

但是在Kotlin中如何实现呢?也是相当简单的,只需要将接收的方法改为内联函数即可。像这样——

inline fun <reified T> uploadingWithKotlin(builder: Builder, onUploadingListener: OnUploadingListener<T>) {
        。
        。
        。
        //网络请求后,已接收到json
        fun onRespone(json:String){
            GsonUtils.parseFromJson(data, T::class.java)//在这里解析,直接可获取到Class
        }
}

 至此,就实现了将JSON直接解析为指定泛型了,而不需要在得到JSON后,然后再重复解析。方便了许多。

 

参考链接:https://blog.csdn.net/changsa65/article/details/78790881

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值