从泛型函数

时间:2016-06-09 13:25:24

标签: generics kotlin

我的问题是关于使用泛型方法的类型推断。

我有以下情况:

interface Obj {
    val Id: String
}
data class User(override val Id: String, val name: String): Obj
data class Case(override val Id: String, val subject: String): Obj

interface Api {
    fun <T: Obj> getLastUpdated(type: KClass<T>, backTill: Duration = Duration.ofDays(1)): LastUpdated

    fun <T: Obj> getDetails(type: KClass<T>, uuid: String): Details<T>

    data class LastUpdatedResponse(val ids: List<String> = emptyList(),
                                   val latestDateCovered: String = "")
    data class LastUpdated(val error: Throwable? = null, val response: LastUpdatedResponse? = null)

    data class DetailsResponse<T>(val wrapped: T)
    data class Details<T>(val error: Throwable? = null, val response: DetailsResponse<T>? = null)
}

在我的测试中,我需要确切地知道模拟的API需要返回什么,因此

val testCase = Case("123", "Testing")
val testUser = User("321", "Dummy")
val mockApi = object: Api {
    override fun <T : Obj> getLastUpdated(type: KClass<T>, backTill: Duration): Api.LastUpdated {
        return Api.LastUpdated(response = Api.LastUpdatedResponse(listOf("123")))
    }

    override fun <T : Obj> getDetails(type: KClass<T>, uuid: String): Details<T> {
        when (type) {
            Case::class -> return Api.Details(response = Api.DetailsResponse(wrapped = testCase)) // <- this fails
            User::class -> return Api.Details(response = Api.DetailsResponse(wrapped = testUser)) // <- as does this
            else -> return Api.Details(error = UnsupportedOperationException())
        }
    }
}

然而,这无法编译:

Error:(114, 43) Kotlin: Type inference failed. Expected type mismatch: inferred type is Api.Details<Case> but Api.Details<T> was expected

我可以使用cast进行操作:

when (type) {
    Case::class -> return Api.Details(response = Api.DetailsResponse(wrapped = testCase)) as Api.Details<T>
    User::class -> return Api.Details(response = Api.DetailsResponse(wrapped = testUser)) as Api.Details<T>

然后我收到一条警告告诉我&#34;这个演员将永远不会成功&#34; - 运行我的测试,它确实按预期工作。

我的问题是 - 为什么没有强制转换这项工作,我应该使用什么呢?

2 个答案:

答案 0 :(得分:1)

方差注释可能对您有所帮助,只需在以下函数中更改泛型参数 T out Obj (在接口和实现者类中):

fun <T: Obj> getDetails(type: KClass<T>, uuid: String): Details<out Obj>

答案 1 :(得分:1)

这可以解决您的问题:

    override fun getDetails(type: KClass<in Obj>, uuid: String): Api.Details<out Obj> {
        when (type) {
            Case::class -> return Api.Details(response = Api.DetailsResponse(wrapped = testCase))
            User::class -> return Api.Details(response = Api.DetailsResponse(wrapped = testUser))
            else -> return Api.Details(error = UnsupportedOperationException())
        }
    }

为了更好地理解它,您可以阅读关于泛型中方差的kotlin文档:https://kotlinlang.org/docs/reference/generics.html

在java中你不能做的事情是:

Collection<String> strings = ...
Collection<Object> objs = strings; // this will fail

在很多情况下,您只想阅读泛型对象,以便进行该分配没有问题。告诉kotlin这是可能的方法是使用输入和输出的单词。在意味着你将阅读泛型而不是写出它们并且意味着你要写它们。