In order to handle Retrofit
api calls I have a sealed interface as below:
sealed interface DataSourceResponseWrapper<out T> {
data class Success<out T>(val result: T) : DataSourceResponseWrapper<T>
data class Error(val throwable: Throwable) : DataSourceResponseWrapper<Nothing>
}
As you can see Success<out T>
is a generic class but Error
is not.
Here is ItemDataRepository
:
class DataRepository constructor(private val itemApiDataSource: ItemApiDataSource) {
suspend fun loadData(search: String, days: Int, aqi: Boolean, alerts: Boolean): DataSourceResponseWrapper<LocalItemResponse> =
coroutineScope {
withContext(Dispatchers.IO) {
val response = try {
DataSourceResponseWrapper.Success(weatherApiDataSource.getWeatherInfo(search, days, if (aqi) "yes" else "no", if (alerts) "yes" else "no"))
} catch (throwable: Throwable) {
DataSourceResponseWrapper.Error(throwable)
}
when (response) {
is DataSourceResponseWrapper.Success<ApiWeatherResponse> ->
DataSourceResponseWrapper.Success(WeatherConverter.convertToLocal(response.result))
is DataSourceResponseWrapper.Error -> response
}
}
}
In DataRepository
class I check if the result is successful or not. If it is, repository class converts the returned ApiItemResponse
to a LocalItemResponse
and returns the result. And if it's not, returns the response
itself
The code works perfectly fine until I change when
statements to the following:
when (response) {
is DataSourceResponseWrapper.Success<ApiWeatherResponse> ->
DataSourceResponseWrapper.Success(WeatherConverter.convertToLocal(response.result))
else -> response
}
It gives me an error saying:
Required:
DataSourceResponseWrapper<LocalItemResponse>
Found:
DataSourceResponseWrapper<Any>
So my question is Why Kotlin does not smartly cast the response as before? And the other question is How can I use else
without needing to check type?
CodePudding user response:
Some cases are simply too sophisticated (too many steps of logic) for the compiler to sort out and infer the types for you. In this case, instead of using an else
branch, you can be specific. This is the way you should be using a sealed
type anyway (no else
branch if it can be avoided):
when (response) {
is DataSourceResponseWrapper.Success<ApiWeatherResponse> ->
DataSourceResponseWrapper.Success(WeatherConverter.convertToLocal(response.result))
is DataSourceResponseWrapper.Error -> response
}
CodePudding user response:
You can in fact write
else -> response as DataSourceResponseWrapper<LocalItemResponse>
to make it work. Although the IDE is likely to warn for an unchecked cast. But it's just a warning. You can safely ignore it because you know for a fact that you can cast it there. It will compile and run just fine. But admittedly it is ugly to have the warning there.