You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 06:06:29