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

如何在Kotlin中声明MutableSharedFlow<EventFilter<T>>泛型变量

问题分析与解决方案

你遇到的错误“Type parameter of a property must be used in its receiver type”,本质是Kotlin不允许顶级属性直接声明泛型参数。泛型参数<T>需要依附于特定上下文(比如泛型类、泛型函数,或是扩展属性的接收者),而顶级属性没有接收者,编译器无法确定这个T的具体类型范围,因此报错。

以下是两种适配你场景的正确实现方案:


方案1:封装到泛型类中(单类型事件总线)

如果你的事件总线只需要处理特定类型的EventFilter,可以把属性和函数封装到泛型类里,让T的作用域与类绑定:

enum class EventType {
    DEFAULT,
    ADD_PLAY_LIST,
    PLAY_PAUSE,
    STOP
}

data class EventFilter<T>(var eventType: EventType = EventType.DEFAULT, var anyObject: T? = null)

class EventBus<T> {
    private val events = MutableSharedFlow<EventFilter<T>>()

    // 推荐让调用者传入协程作用域,避免内存泄漏
    fun startListening(scope: CoroutineScope, onReceivedEvent: (event: EventFilter<T>) -> Unit) {
        scope.launch(Dispatchers.Main) {
            events.collectLatest(onReceivedEvent)
        }
    }

    suspend fun sendEvent(event: EventFilter<T>) {
        events.emit(event)
    }
}

// 使用示例
val playlistBus = EventBus<Playlist>()
val playerBus = EventBus<PlayerState>()

// 在ViewModel/Activity中监听
playlistBus.startListening(viewModelScope) { event ->
    // 处理播放列表类型的事件
}

// 发送事件
playlistBus.sendEvent(EventFilter(EventType.ADD_PLAY_LIST, Playlist("My Favorite")))

方案2:支持任意类型的全局事件总线

如果需要一个能处理所有类型EventFilter的全局总线,可以让SharedFlow的类型为EventFilter<*>,再通过inline reified泛型实现类型安全的监听:

enum class EventType {
    DEFAULT,
    ADD_PLAY_LIST,
    PLAY_PAUSE,
    STOP
}

data class EventFilter<T>(var eventType: EventType = EventType.DEFAULT, var anyObject: T? = null)

private val globalEvents = MutableSharedFlow<EventFilter<*>>()

// 只监听匹配泛型T的事件
inline fun <reified T> startGlobalListening(scope: CoroutineScope, onReceivedEvent: (event: EventFilter<T>) -> Unit) {
    scope.launch(Dispatchers.Main) {
        globalEvents.collectLatest { event ->
            if (event.anyObject is T) {
                @Suppress("UNCHECKED_CAST")
                onReceivedEvent(event as EventFilter<T>)
            }
        }
    }
}

suspend fun <T> sendGlobalEvent(event: EventFilter<T>) {
    globalEvents.emit(event)
}

// 使用示例
startGlobalListening<Playlist>(lifecycleScope) { event ->
    // 仅处理Playlist类型的事件
}

sendGlobalEvent(EventFilter(EventType.PLAY_PAUSE, PlayerState(isPlaying = true)))

注意点

  1. 避免在函数内部创建CoroutineScope(如你原代码中的CoroutineScope(Dispatchers.Main).launch),这样的作用域无法被正确管理,容易引发内存泄漏。推荐使用组件自带的作用域(如viewModelScope、lifecycleScope)。
  2. 方案2中使用inline reified可以在编译期确定泛型类型,实现类型安全的事件过滤,无需强制类型转换的风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 04:40:24