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

如何获取Consumer参数中事件对象的类?事件系统实现求助

事件系统实现问题

我正在实现一个包含多种事件类型的事件系统,例如ExampleEvent extends Event。希望通过lambda/Consumer确定传入hook函数的事件类型,使用如下语法:

hook(
    (ExampleEvent e) -> e.example(),
    (ExampleEvent2 e) -> // code here
);

目前我的代码实现如下,需要将这些consumer存入map以便在事件触发时执行,但无法获取lambda对应的事件类:

static Map<Class, ArrayList<Consumer<? extends Event>>> hooks = new HashMap<>();

public static void hook(Consumer<? extends Event> ...consumers) {
    for(Consumer<? extends Event > fn : consumers) {
        final Class klass = // 需要获取lambda中的事件类

        if(hooks.containsKey(klass)) {
            hooks.get(klass).add(fn);
        } else {
            hooks.put(klass, new ArrayList<>(fn));
            hooks.get(klass).add(fn);
        }
    }
}

public static void fire(Event event) {
    ArrayList<Consumer<? extends Event>> classHooks = hooks.get(event.getClass());
    if(classHooks == null) {
        return;
    }

    for(Consumer c : classHooks) {
        c.accept(event);
    }
}

尝试了一些方法但未能成功实现,请问是否有可行的实现方式?或者有更优的方案?


可行实现方案

方案1:泛型方法+显式类型参数(推荐)

这是最可靠、无反射的方案,通过明确指定事件类型,避免从lambda中推断类型的麻烦:

static Map<Class<? extends Event>, List<Consumer<? extends Event>>> hooks = new ConcurrentHashMap<>();

// 单consumer添加
public static <T extends Event> void hook(Class<T> eventType, Consumer<T> consumer) {
    hooks.computeIfAbsent(eventType, k -> new ArrayList<>()).add(consumer);
}

// 批量consumer添加重载
public static <T extends Event> void hook(Class<T> eventType, Consumer<T>... consumers) {
    List<Consumer<? extends Event>> hookList = hooks.computeIfAbsent(eventType, k -> new ArrayList<>());
    Collections.addAll(hookList, consumers);
}

// 类型安全的事件触发
@SuppressWarnings("unchecked")
public static void fire(Event event) {
    List<Consumer<? extends Event>> eventHooks = hooks.get(event.getClass());
    if (eventHooks == null) return;
    for (Consumer<? extends Event> hook : eventHooks) {
        ((Consumer<Event>) hook).accept(event);
    }
}

使用方式调整为:

hook(ExampleEvent.class, (ExampleEvent e) -> e.example());
hook(ExampleEvent2.class, (ExampleEvent2 e) -> { /* 业务代码 */ });

该方案优势:

  • 编译期类型检查,避免运行时错误
  • 不依赖反射或JVM内部实现,兼容性强
  • 代码意图清晰,易于维护

方案2:反射读取lambda参数类型(有局限)

如果坚持保留原语法,可以通过反射解析lambda的方法签名,但依赖JVM实现细节,存在兼容性风险,且要求lambda可序列化:

@SuppressWarnings("unchecked")
private static <T extends Event> Class<T> getEventTypeFromLambda(Consumer<T> consumer) {
    if (!(consumer instanceof Serializable)) {
        throw new IllegalArgumentException("Lambda必须实现Serializable接口");
    }
    try {
        Method writeReplace = consumer.getClass().getDeclaredMethod("writeReplace");
        writeReplace.setAccessible(true);
        SerializedLambda lambda = (SerializedLambda) writeReplace.invoke(consumer);
        String methodSig = lambda.getImplMethodSignature();
        // 解析方法签名中的参数类型
        String paramType = methodSig.substring(methodSig.indexOf("(")+1, methodSig.indexOf(")"))
                .replace("/", ".")
                .replaceFirst("^L", "")
                .replaceFirst(";$", "");
        return (Class<T>) Class.forName(paramType);
    } catch (Exception e) {
        throw new RuntimeException("无法从Lambda中提取事件类型", e);
    }
}

// 修改原hook方法
public static void hook(Consumer<? extends Event>... consumers) {
    for (Consumer<? extends Event> fn : consumers) {
        Class<? extends Event> eventType = getEventTypeFromLambda(fn);
        hooks.computeIfAbsent(eventType, k -> new ArrayList<>()).add(fn);
    }
}

使用时需要给lambda加上Serializable标记:

hook(
    (Serializable & Consumer<ExampleEvent>) e -> e.example(),
    (Serializable & Consumer<ExampleEvent2>) e -> { /* 业务代码 */ }
);

注意:该方案依赖JVM私有API,不同版本可能失效,不推荐生产环境使用。


更优方案建议

优先选择方案1,同时可以扩展以下优化点:

  • 用ConcurrentHashMap替代HashMap,解决并发场景下的线程安全问题
  • 支持事件继承:触发子类事件时,同时执行父类对应的hook(可通过遍历事件类的继承链实现)
  • 给fire方法添加异常捕获,避免单个hook执行失败导致整个事件流程中断

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 01:40:32