如何在 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)
现在我可以按我想要的方式调用函数了。
我正在尝试实现一个像这样的通用 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)
现在我可以按我想要的方式调用函数了。