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

泛型密封类Resource的Flow回调类型匹配及通配符使用问题

问题:优化Resource Flow的回调类型匹配

现有可运行代码

Resource密封类

sealed class Resource<out T:Any>{
    data class Success<out T:Any> (val data:T):Resource<T>()
    data class Error(val exception: Exception):Resource<Nothing>()
    data class Loading(val message:String):Resource<Nothing>()
}

Flow相关代码

fun simple(): Flow<Resource<String>> = flow {
    delay(100)
    emit(Resource.Loading("Loading message..."))
    delay(100)
    emit(Resource.Error(Exception("Error Message")))
    delay(100)
    emit(Resource.Success("Value=X"))
}

suspend fun <T> Flow<T>.process(
    onLoading: ((T)->Unit),
    onError: ((T)->Unit),
    onSuccess: ((T)->Unit)) {
    collect { value ->
        when ( value ) {
            is Resource.Error -> {
                onError(value)
            }
            is Resource.Loading -> {
                onLoading(value)
            }
            is Resource.Success<*> -> {
                onSuccess(value)
            }
        }
    }
}

fun job1() {
    viewModelScope.launch {
        simple().process({

        }, {

        }, {

        })
    }
}

需求

当前process函数的三个回调均接收Resource<String>类型,希望实现:

  • onLoading回调接收Resource.Loading类型
  • onError回调接收Resource.Error类型
  • onSuccess回调接收Resource.Success<String>类型

期望调用形式:

fun job1() {
    viewModelScope.launch {
        simple().process(
            { loading -> /* loading是Resource.Loading类型 */ },
            { error -> /* error是Resource.Error类型 */ },
            { success -> /* success是Resource.Success<String>类型 */ }
        )
    }
}

同时有疑问:编译器强制要求写Resource.Success<*>,否则报错,这样编写是否存在问题?


解决方案

优化后的process扩展函数

通过绑定Flow的泛型与Resource的泛型约束,实现精准的回调类型匹配:

suspend fun <T : Any> Flow<Resource<T>>.process(
    onLoading: (Resource.Loading) -> Unit,
    onError: (Resource.Error) -> Unit,
    onSuccess: (Resource.Success<T>) -> Unit
) {
    collect { resource ->
        when (resource) {
            is Resource.Loading -> onLoading(resource)
            is Resource.Error -> onError(resource)
            is Resource.Success -> onSuccess(resource) // 编译器自动推导为Success<T>,无需写<*>
        }
    }
}

调用示例

fun job1() {
    viewModelScope.launch {
        simple().process(
            onLoading = { loading ->
                println("加载中:${loading.message}")
            },
            onError = { error ->
                println("错误:${error.exception.message}")
            },
            onSuccess = { success ->
                println("成功:${success.data}")
            }
        )
    }
}

关键说明

  1. 泛型约束调整:将扩展函数的泛型限定为<T : Any> Flow<Resource<T>>,明确Flow的元素是Resource<T>,让编译器能自动推导Success的具体类型,无需通配符<*>。
  2. 类型安全保障:密封类的特性让when分支的类型判断完全可靠,编译器会自动确认每个分支的具体子类类型,无需手动类型转换。

关于Resource.Success<*>的疑问

原代码中必须写Resource.Success<*>,是因为泛型<T>未与Resource的泛型绑定,编译器无法确定Success的具体类型,只能用通配符兜底。这种写法本身合法,但会丢失类型信息——回调中无法直接访问data的具体类型,必须强转,存在运行时类型转换异常的风险。优化后的写法通过泛型绑定解决了这个问题,既保证类型安全,又无需额外转换。

对尝试代码的分析

  • Edit_1代码问题:泛型<T: Resource<Any>>约束过于宽泛,且onSuccess的T无法与Resource.Success的泛型关联,导致编译器无法确认类型匹配,出现类型不兼容报错。
  • 强制类型转换的隐患:强转能消除编译报错,但会绕过编译器的类型检查,运行时若类型不匹配会抛出ClassCastException,不推荐使用。

内容的提问来源于stack exchange,提问作者Metropol_Tilkisi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 12:10:26