繁体   English   中英

如何在 Kotlin 接口的实现中编写泛型函数

[英]How to write generic functions in Kotlin interfaces's implementations

我正在尝试实现一个像这样的通用 HttpClient:

interface HttpClient {
    fun <T: Any> get(url: String): T?
}

由 class 实现,如下所示:

class HttpClientImpl @Inject constructor(...) : HttpClient {

    override fun <T : Any> get(url: String): T? = execute(url)
    
    private inline fun <reified T: Any> execute(url: String): T? {
        val request = Request.Builder().url(url).get().build()
        client.newCall(request).execute().use {
            return it.body?.parseBodySuccess()
        }
    }

    private inline fun <reified T: Any> ResponseBody?.parseBody(): T? {
        val type = objectMapper.typeFactory.constructType(T::class.java)
        return this?.let { objectMapper.readValue(it.string(), type) }
    }

}

现在,我希望能够以这种方式调用这样的 GET 方法:

data class MyEntity(...)

class MyService @Inject constructor(private val client: HttpClient) {
    fun performGet(url: String): MyEntity? = client.get<MyEntity>(url)
}

然而,这是不允许的,编译器会抛出一个引用代码行的错误

override fun <T : Any> get(endpoint: String): T? = execute(endpoint)

标记为:不能使用“T”作为具体化类型参数。 请改用 class。

我一直在尝试将该行重写为

override inline fun <reified T : Any> get(endpoint: String): T? = execute(endpoint)

然而,尽管必须将其他两个内联函数设置为“非私有”,编译器仍然无法编译,因为在最后一种编写覆盖 function 的方法中,它说:

由具有具体化类型参数的 function 覆盖

我怎样才能实现这样的通用 function?

我最终做了这样的事情:

interface HttpClient {
    fun <T: Any> get(url: String, type: Class<T>): T?
}

实现为:

class HttpClientImpl @Inject constructor(...) : HttpClient {

    override fun <T : Any> get(url: String, type: Class<T>): T? = execute(url, type)
    
    private fun <T: Any> execute(url: String, type: Class<T>): T? {
        val request = Request.Builder().url(url).get().build()
        client.newCall(request).execute().use {
            return it.body?.parseBody(type)
        }
    }

    private fun <T: Any> ResponseBody?.parseBody(type: Class<T>): T? {
        val dataType = objectMapper.typeFactory.constructType(type)
        return this?.let { objectMapper.readValue(it.string(), dataType) }
    }

}

我可以这样调用:

data class MyEntity(...)

class MyService @Inject constructor(private val client: HttpClient) {
    fun performGet(url: String): MyEntity? = client.get(url, MyEntity::class.java)
}

我宁愿将类型直接作为实际类型传递,例如

client.get<MyEntity>(url)

而不是将 Class 作为参数传递,但是,只是现在它可以工作......

如果有人可以建议更好的方法,请告诉我。

更新

正如 Pawel 所建议的,我已经为 HttpClient 接口创建了一个额外的内联扩展 function

inline fun <reified T:Any> HttpClient.get (url: String) = get(url, T::class.java)

我现在可以按照我想要的方式拨打 function。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM