Devolución de instancias específicas de la function genérica

Mi pregunta es acerca de la inferencia de tipo con methods generics.

Tengo el siguiente escenario:

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 latestDateCovenetworking: 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) } 

En mis testings, necesito saber exactamente qué debe devolver la API burlada, por lo tanto

 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()) } } } 

Sin embargo, esto no se puede comstackr con:

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

Puedo hacer que funcione con el casting:

 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> 

Pero luego recibo una advertencia informándome que "este elenco nunca tendrá éxito", ejecutando mis testings, funciona y funciona como se esperaba.

Mi pregunta es: ¿por qué esto no funciona sin el lanzamiento y qué debería usar en su lugar?

La anotación de varianza podría ayudarlo, simplemente cambie el parámetro genérico T a Out Obj en la siguiente function (tanto en la interfaz como en la class de implementador):

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

Esto debería solucionar tu problema:

  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()) } } 

Para entenderlo mejor, puede leer la documentation de kotlin sobre varianza en generics: https://kotlinlang.org/docs/reference/generics.html

Hay cosas que no puedes hacer en Java como:

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

Hay muchos casos en los que solo desea leer los objects generics para que no haya ningún problema al realizar esa tarea. La forma de decirle a kotlin que esto es posible es usar las palabras dentro y fuera. Significa que leerás los generics pero no los escribirás y significarás que los escribirás.

  • Dagger2 inyecta un presentador en un error de actividad de Kotlin
  • Spring Data JPA Cómo utilizar Kotlin nulls en lugar de Opcional
  • ¿Cuál es la diferencia entre shouldbe y shouldbee exactamente en KotlinTest?
  • ¿Qué tipo de dependencia tiene el pepino kotlin?
  • Cómo get la position de OnScroll en WebView Android
  • Observa Inyectar error final privado
  • Cómo crear una list con un argumento genérico en Kotlin
  • Polimorfismo en las funciones de extensión en Kotlin
  • Boolean - Conversión de int en Kotlin
  • Obtener class de list con generics, por ejemplo: Lista <Número> :: class
  • Spring Boot: no puede include recurso estático