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

Kotlin EventEmitter类:如何存储不同类型事件监听器?

实现Kotlin EventEmitter:替换TODO及函数存储方案

一、替换代码中的TODO

先拆解你代码里的两个TODO,一步步修正:

1. listeners的类型定义

一个事件通常可以绑定多个监听器,所以不能直接存单个函数,应该用监听器列表来存储。这里的TODO要替换为MutableList<(Any?) -> Unit>,最终listeners的定义是:

private val listeners: MutableMap<String, MutableList<(Any?) -> Unit>> = mutableMapOf()

用(Any?) -> Unit是因为不同事件可能接收不同类型的参数,这样能兼容所有类型的监听器函数。

2. emit函数的实现

emit的核心是触发指定事件的所有监听器,并返回是否存在该事件的监听器。实现逻辑如下:

  • 先从listeners中获取当前事件的监听器列表
  • 如果列表为空,直接返回false
  • 遍历列表中的每个监听器,调用并传入参数arg
  • 最后返回true

同时还要修正on函数的问题:原来的listeners.set(event, listener)会直接覆盖之前的监听器,正确的做法是先获取或创建事件对应的列表,再添加新的监听器,还要处理maxListeners的限制(超过数量时打印警告)。

修正后的完整代码片段:

open class EventEmitter(
    var maxListeners: Int = defaultMaxListeners
) {
    companion object {
        const val defaultMaxListeners = 10
    }

    private val listeners: MutableMap<String, MutableList<(Any?) -> Unit>> = mutableMapOf()

    fun <T> on(event: String, listener: (T) -> Unit) {
        val eventListeners = listeners.getOrPut(event) { mutableListOf() }
        // 处理最大监听器数量限制
        if (eventListeners.size >= maxListeners) {
            println("Warning: Event '$event' has reached max listeners ($maxListeners)")
        }
        // 将泛型监听器转换为兼容类型添加到列表
        eventListeners.add { arg -> listener(arg as T) }
    }

    fun <T> emit(event: String, arg: T): Boolean {
        val eventListeners = listeners[event] ?: return false
        // 遍历调用所有监听器
        eventListeners.forEach { it(arg) }
        return true
    }
}

二、其他存储相似函数的方法

除了上面用MutableMap<String, MutableList<...>>的基础方案,还有几种常见的替代方式:

  • 使用Multimap:如果不想手动维护Map+List的结构,可以用Guava库的Multimap<String, (Any?) -> Unit>,它本质是对“键对应多个值”的封装,能简化添加、删除监听器的操作。
  • 封装监听器接口:定义一个专门的函数式接口,比如:
    fun interface EventListener<T> {
        fun onEvent(arg: T)
    }
    
    然后存储MutableMap<String, MutableList<EventListener<*>>>,这种方式类型更明确,适合需要统一管理监听器生命周期的场景。
  • 弱引用存储:如果担心监听器导致内存泄漏,可以用MutableList<WeakReference<(Any?) -> Unit>>存储监听器的弱引用,每次调用前先清理已被回收的引用。
  • 协程Channel(异步场景):如果是异步事件场景,可以为每个事件创建一个Channel<T>,监听器通过receive接收事件,emit则通过send发送事件,这种方式天然支持异步处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:14:53