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.

  • TornadoFX - eliminar elemento con ContextMenu hacer clic derecho
  • Anko + Content Provider, no actualiza los datos sobre el cambio
  • Kotlin - reference no resuelta por constante
  • Cómo lanzar Cadena en Int y Largo?
  • Mockito querido pero no invocado
  • No se puede inyectar la misma instancia en un Servicio y un ViewModel
  • Click listener en ViewHolder solo responde a veces
  • Compruebe si la class es un valor válido para KParameter
  • Fusionar datos de diferentes Observables y elegir diferentes estrategias de búsqueda, según la disponibilidad de datos
  • Prueba mónada por kotlin
  • ¿Puedo agregar operadores a las classs existentes?