如何使嵌套通用参数系统正常工作?

所以我试图让一个相当复杂的系统工作。 这是我正在尝试的基础知识。

规则:

abstract class Rule { // stuff } class ExampleRule extends Rule { // stuff } 

处理程序:

 abstract class RuleHandler { Class clazz; RuleHandler(Class forClass) { this.clazz = forClass; } abstract void doStuff(T rule); } class ExampleRuleHandler extends RuleHandler { ExampleRuleHandler() { super(ExampleRule.class); } void doStuff(ExampleRule rule) { // stuff } } 

把它们绑在一起:

 class HandlerDispatcher { Map<Class, RuleHandler> handlers; void register(RuleHandler handler) { handlers.put(handler.clazz, handler); } void doStuff(List rules) { for(Rule rule : rules) { RuleHandler handler = handlers.get(rule.getClass()); handler.doStuff(rule); } } } class Test { void main() { HandlerDispatcher hd = new HandlerDispatcher(); hd.register(new ExampleRuleHandler()); } } 

到目前为止,我已经尝试了不同参数的各种组合(通配符,受限制等),并且还没有得到这种编译而没有与类型相关的错误。 欢迎任何见解,解决方案或替代方法。

您正试图以运行时方式使用generics。 如果您在编译时不知道需要处理的类型(无论是真实类型还是类型参数本身),您都不能使用generics,简单明了。 它只是一个编译时间(或大部分)构造。

在这里,你试图通用和动态地处理事情。 这通常是不可能的。 使用原始类型并使用不安全的类型。

我自己,我会说你只是在问这个问题:

 abstract class RuleHandler { abstract void doStuff(T rule); } 

就这样说吧:

 abstract class RuleHandler { abstract void doStuff(Rule rule); } 

然后只注册它可以处理的类型的规则处理程序。

编辑

根据您的评论,您的目标似乎是在界面用户上强制执行您的设计。 我坚持认为你应该将过滤和处理的概念分开。

也就是说,另一种选择是将类令牌从处理程序本身中取出并使您的寄存器方法生成通用。

 interface RuleHandler { void doStuff(T rule); } //... public  void register(Class type, RuleHandler handler) { map.put(type, handler); } public void process() { for ( Rule r : rules ) { for(Map.Entry, RuleHandler> entry : map.entrySet() ) { if ( entry.getKey().instanceOf(r) ) { @SuppressWarnings("unchecked") ((RuleHandler)entry.getValue()).doStuff(r); } } } } 

在这里,我们在使用原始类型RuleHandler时禁止警告。 我们知道只通过检查,通过查看对map访问并看到该类始终与RuleHandler类型参数匹配是RuleHandler 。 但是,如果客户端在调用register()时没有类型安全警告(即他们将调用参数化为register() ,这显然是安全的。

(在for get中添加了内部for循环,以便为给定Rule子类的子类找到处理程序)

你无法避免在这里取消选中。

handlers映射中的每个条目都将ClassRuleHandler ,对于某些类T ,每个条目都不同。 Map的方法不表示这种限制。

您可以做的是创建Map的子类,该子类强制每个条目的类型一致性,并在新的地图类中执行所有未经检查的强制转换。

例如,看看Guava的ClassToInstanceMap 。

观察那个

  • 处理程序是私人的和最终的
  • handler.clazz是最终的
  • RuleHandler暗示RuleHandler === RuleHandler RuleHandler === RuleHandler

以下代码是正确的(并编译)

 abstract class RuleHandler { final Class clazz; // as before } class HandlerDispatcher { private final Map, RuleHandler> handlers; void register(RuleHandler handler) { handlers.put(handler.clazz, handler); } void doStuff(List rules) { for(Rule rule : rules) { @SuppressWarnings("unchecked") RuleHandler handler = (RuleHandler) handlers.get(rule.getClass()); handler.doStuff(rule); } } } class Test { void main() { HandlerDispatcher hd = new HandlerDispatcher(); hd.register(new ExampleRuleHandler()); RuleHandler handler = new ExampleRuleHandler(); hd.register(handler); } } 

问题是这一行。

 RuleHandler handler = handlers.get(rule.getClass()); 

编译器不知道<? extends Rule>是正确的类,因为您查找了Rule类的正确处理程序。 你可以替换

 RuleHandler handler = handlers.get(rule.getClass()); 

这将产生警告,因为编译器不知道在运行时,您将选择正确的类型。 如果这困扰你,你可以加入你的class级。

 @SuppressWarnings("unchecked")