如何在 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)

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

被具有具体化类型参数的函数覆盖

如何实现这样的通用功能?

我最终做了这样的事情:

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 接口创建了一个额外的内联扩展函数

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

现在我可以按我想要的方式调用函数了。