从 class 调用 <reified T> 内联函数

calling <reified T> inline functions from a class

简而言之:我想创建一个 T 类型的泛型 class,它应该调用 T 类型的具体化内联函数,以便能够更通用地使用 Gson

详情:
你好。我最近开始将 kotlin 用于 Android 开发,并且我正在尝试创建一个通用网络客户端 class。 class 将从服务器获取数据,将响应转换为通用 class 类型和 return 响应提供的数据类型,如下所示:

class GenericNetworkService<RESPONSE_TYPE> {

    fun getDataFromServer(url: String): RESPONSE_TYPE {
        val request = Request.Builder().url(url).build()

        val response = OkHttpClient().newCall(request).execute()

        val convertor = Gson()

        val result: RESPONSE_TYPE = convertor.fromJson(response.body!!.string(), RESPONSE_TYPE)

        return result

    }
}

//calling
//fun main() {

//    val listOfPersons = GenericNetworkService<List<Person>>().getDataFromServer(url1)
//    println(listOfPersons)

//    val listOFTeacher = GenericNetworkService<List<Teacher>>().getDataFromServer(url2)
//    println(listOFTeacher)
// }

这里,只有 gson.fromJson() 函数才是真正需要最终输出必须转换的 class 细节的函数。 但是它没有将泛型类型作为第二个参数,所以上面的程序无法运行。 在搜索了一些互联网文章后,我发现了这个TypeToken Api 这在一定程度上有助于实现通用行为:


inline fun <reified T> fromJson(json: String): T {
   return Gson().fromJson(json, object: TypeToken<T>(){}.type)
}

//calling : 
// val data : List<Teacher> = fromJson(response)

使用内联具体化函数,我能够实现我最初想要的一切:


private inline fun <reified RESPONSE_TYPE> getGenericRespSync(url: String): RESPONSE_TYPE {
    val okHttpClient = generateNetworkClient()
    val request = Request.Builder().url(url).build()
    val response: Response = okHttpClient.newCall(request).execute()

    val body: Reader = response.body!!.charStream()
    val finalResponse: RESPONSE_TYPE =  getConvertedListForResponse(body) //this is the same reified inline function as above with some non signinficant additions
    return finalResponse
}


但是我希望我的其他 classes 通过一些 class 实例调用这个函数。即 GenericNetworkService<List<Person>>().getGenericRespSync(url1) 之类的东西应该是 returning List 而不是直接调用 getGenericRespSync(url1) 。我怎样才能实现这样的功能?

嗯,你可以定义

inline fun <reified RESPONSE_TYPE> GenericNetworkService<RESPONSE_TYPE>.getGenericRespSync1(url: String): RESPONSE_TYPE = getGenericRespSync<RESPONSE_TYPE>(url)

(我以不同的方式命名该函数,因此它可以调用您之前定义的 getGenericRespSync)。当然,它只使用调用它的实例来获取其类型参数,否则将忽略它。

不幸的是,class 构造函数不能使用与内联函数相同的 reified 机制。但是,您可以将类型定义为常规构造函数参数,并使用顶级内联函数

创建 class 的新实例
class GenericNetworkService<RESPONSE_TYPE>(private val responseTypeClass: Class<RESPONSE_TYPE>) {

    fun getDataFromServer(url: String): RESPONSE_TYPE {
        val request = Request.Builder().url(url).build()

        val response = OkHttpClient().newCall(request).execute()

        val convertor = Gson()

        val result: RESPONSE_TYPE = convertor.fromJson(response.body!!.string(), responseTypeClass)

        return result

    }
}

inline fun <reified RESPONSE_TYPE> createService() = GenericNetworkService(RESPONSE_TYPE::class.java)

当然,你也可以将createService重命名为GenericNetworkService,看起来像一个构造函数。这在 kotlin 中变得越来越普遍,特别是如果您查看协程核心代码,所以我不会再将其视为 anti-pattern。