Возврат определенных экземпляров из общей функции

Мой вопрос касается вывода типа с помощью общих методов.

У меня есть следующий сценарий:

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 

Я могу заставить его работать с кастингом:

 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> 

Но потом я получаю предупреждение, информирующее меня о том, что «этот актер никогда не преуспеет» – запуск моих тестов, он работает и работает так, как ожидалось.

Мой вопрос: почему это не работает без кастинга и что я должен использовать вместо этого?

Разница в вариациях может помочь вам, просто измените общий параметр T на Obj в следующей функции (как в интерфейсе, так и в классе реализации):

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

Это должно решить вашу проблему:

  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 

Существует много случаев, когда вы хотите читать только объекты generics, поэтому нет никаких проблем для выполнения этого назначения. Способ сказать kotlin это возможно, используя слова в и из. В средствах вы собираетесь читать дженерики, но не писать их, а значит, вы собираетесь писать их.

Intereting Posts