Kotlin泛型类型问题:如何解决Resource类的unchecked cast异常
问题场景与代码定义
1. Resource状态类定义
data class Resource<out T>( val status: Status, val data: T? ) { sealed class Status { data class Success<out T>(val data: T?) : Status() data class Error( val message: String, val statusCode: Int ) : Status() object Loading : Status() } companion object { fun <T> success(data: T?): Resource<T> { return Resource(status = Status.Success(data), data = data) } fun <T> error(msg: String, statusCode: Int, data: T? = null): Resource<T> { return Resource(status = Status.Error(msg, statusCode), data = data) } fun <T> loading(data: T? = null): Resource<T> { return Resource(status = Status.Loading, data = data) } } }
2. 网络请求映射函数networkResource
inline fun <REMOTE> networkResource( crossinline fetchFromRemote: suspend () -> Flow<ApiResponse<REMOTE>>, crossinline onFetchFailed: (errorBody: String?, statusCode: Int) -> Unit = { _: String?, _: Int -> } ) = flow<Resource<REMOTE>> { emit(Resource.loading(null)) fetchFromRemote().collect { apiResponse -> when (apiResponse) { is ApiSuccessResponse -> { apiResponse.body?.let { emit(Resource.success(data = it)) } } is ApiErrorResponse -> { onFetchFailed(apiResponse.errorMessage, apiResponse.statusCode) emit(Resource.error(msg = apiResponse.errorMessage, statusCode = apiResponse.statusCode, data = null)) } is ApiEmptyResponse -> { emit(Resource.success(data = null)) } } } }
3. 函数调用示例
fun getSomething(): Flow<Resource<SuccessResponse<GetCartResponse>>> { return networkResource( fetchFromRemote = { cartApi.getCart() }, onFetchFailed = { _, _ -> } ).flowOn(Dispatchers.IO) }
4. 遇到的类型转换问题
在收集该Flow时,尝试从Status中获取泛型数据出现Unchecked cast警告:
getSomething().collect { response -> when (response.status) { is Resource.Status.Loading -> { Resource.loading(null) } is Resource.Status.Success<*> -> { // 这里出现Unchecked cast警告: Resource.Status to Resource.Status.Success<Quote> val quote = response.status as Resource.Status.Success<Quote> Resource.success(quote.data) } is Resource.Status.Error -> { val error = response.status as Resource.Status.Error Resource.error(error.message, error.statusCode) } } }
解决方案
方案1:直接使用Resource外层的data字段
Resource类在创建时,已将Status.Success的data同步到外层的data属性中,无需从Status里取值,直接使用response.data即可获得正确类型:
getSomething().collect { response -> when (response.status) { is Resource.Status.Loading -> { Resource.loading(null) } is Resource.Status.Success<*> -> { // 类型自动推导为SuccessResponse<GetCartResponse>? val cartData = response.data Resource.success(cartData) } is Resource.Status.Error -> { val error = response.status as Resource.Status.Error Resource.error(error.message, error.statusCode) } } }
方案2:利用when智能转换避免强制类型转换
若一定要从Status.Success中获取数据,调整when分支写法,让编译器自动推导类型,同时用安全转换避免异常:
getSomething().collect { response -> when (val status = response.status) { Resource.Status.Loading -> { Resource.loading(null) } is Resource.Status.Success<*> -> { // 安全转换为目标类型,避免ClassCastException val quote = status.data as? Quote quote?.let { Resource.success(it) } ?: Resource.error("类型转换失败", 400) } is Resource.Status.Error -> { Resource.error(status.message, status.statusCode) } } }
方案3:优化Resource类的泛型一致性
当前Status.Success单独定义泛型,易与外层Resource泛型混淆。修改Status定义,复用外层泛型,彻底避免类型转换问题:
data class Resource<out T>( val status: Status, val data: T? ) { sealed class Status { // 不再单独定义泛型,复用外层Resource的T object Success : Status() data class Error(val message: String, val statusCode: Int) : Status() object Loading : Status() } companion object { fun <T> success(data: T?): Resource<T> { return Resource(status = Status.Success, data = data) } // error、loading方法保持不变 } }
修改后直接通过外层data获取数据,无类型转换风险。
内容的提问来源于stack exchange,提问作者Annon
相关产品推荐
相关产品推荐

