我正在为参数化事件编写一个简单的事件系统,它使用Map
从类到Set
的处理程序,这些处理程序将该类作为其参数。我的理解是I can't define that relationship between key and value types via parameter restrictions并且我得到了未经检查的强制警告,将元素拉出集合。
这是我目前所拥有的:
public class Manager {
private class Event<T> {
// getSubject(), etc.
}
private interface Handler<T> {
public T handleEvent(Event<T> event, T subject);
}
private final Map<Class<?>, Set<Handler<?>>> handlers = new HashMap<Class<?>, Set<Handler<?>>>();
public <T> void post(final Event<T> event, final T subject) {
final Class<?> type = subject.getClass();
if (this.handlers.containsKey(type)) {
for (Handler<?> handler : this.handlers.get(type)) {
// unchecked cast
((Handler<T>) handler).handleEvent(event, subject);
}
}
}
public <T> void register(final Class<T> type, final Handler<T> handler) {
if (!this.handlers.containsKey(type)) {
this.handlers.put(type, new HashSet<Handler<?>>());
}
this.handlers.get(type).add(handler);
}
}
是否可以避免这种未经检查的演员?我的设计中可能存在缺陷吗?
我在这里和Google上花了很长时间,但却无法找到任何涵盖这种安排的内容。
答案 0 :(得分:1)
如果事件类本身形成层次结构,则可以通过使用访问者模式来避免强制转换。它在Java中非常麻烦,但没有必要的演员表。
答案 1 :(得分:1)
不幸的是,在这种情况下你只能做@SuppressWarnings("unchecked")
。
地图声明中没有任何内容可以阻止您将Class<A>
与Set<Handler<B>>
相关联,因此编译器无法知道是否是这种情况。遗憾的是,没有办法对java.util.Map
的任何实施施加这样的限制。
如果要实现允许此强制执行的自己的数据结构(提供Handler<T> getHandler(Class<T> type)
等方法),您最终只会在该数据结构中隐藏未经检查的强制转换并保持客户端代码清洁。
作为旁注,Map
界面需要Object
作为get()
,containsKey()
等的参数,因此所有通用内容都会丢失(甚至如果你可以将密钥的泛型类型与其对应的值相关联,这是不可能的。)
答案 2 :(得分:0)
我的代码组织有点不同,Code for Broadcasters, the equivalent of your Manager, here但在我的post()代码中,我做的相当于
for (Handler<T> handler : this.handlers.get(type)) {
do stuff ...
}
由于事件参数由T参数化,因此您可以将<T>
添加到处理程序。但我通过猜测和浮躁来做很多花哨的仿制药,YMMV ...: - )
P.S。你的register()方法应该同步。