如何获取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
相关产品推荐
相关产品推荐

