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

时间:2010-12-08 17:44:24

标签: java generics nested-generics

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

规则:

abstract class Rule
{
  // stuff
}

class ExampleRule extends Rule
{
  // stuff
}

处理程序:

abstract class RuleHandler<T extends Rule>
{
  Class<T> clazz;
  RuleHandler(Class<T> forClass)
  {
    this.clazz = forClass;
  }
  abstract void doStuff(T rule);
}

class ExampleRuleHandler extends RuleHandler<ExampleRule>
{
  ExampleRuleHandler()
  {
    super(ExampleRule.class);
  }
  void doStuff(ExampleRule rule)
  {
    // stuff
  }
}

将他们捆绑在一起:

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

class Test
{
  void main()
  {
    HandlerDispatcher hd = new HandlerDispatcher();
    hd.register(new ExampleRuleHandler());
  }
}

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

4 个答案:

答案 0 :(得分:4)

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

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

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

abstract class RuleHandler<T extends Rule>
{
  abstract void doStuff(T rule);
}

说实话:

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

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

修改

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

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

interface RuleHandler<T extends Rule> {
    void doStuff(T rule);
}

//...

public <T> void register(Class<T> type, RuleHandler<? super T> handler) {
   map.put(type, handler);
}

public void process() {
   for ( Rule r : rules ) {
       for(Map.Entry<Class<?>, RuleHandler<?>> entry : map.entrySet() ) {
           if ( entry.getKey().instanceOf(r) ) {
               @SuppressWarnings("unchecked")
               ((RuleHandler)entry.getValue()).doStuff(r);
           }
       }
   }
}

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

(在get上添加了内部for循环,这样就可以找到给定Rule子类的子类的处理程序)

答案 1 :(得分:1)

你无法避免在此处取消选中。

handlers地图中的每个条目都会将Class<T>RuleHandler<T>相关联,对于某些类T,每个条目都有所不同。 Map的方法没有表达这种限制。

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

例如,请查看GuavaClassToInstanceMap

答案 2 :(得分:1)

观察

  • 处理程序是私有的和最终的
  • handler.clazz是最终的
  • RuleHandler<T extends Rule>隐含RuleHandler<?> === RuleHandler<? extends Rule>

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

abstract class RuleHandler<T extends Rule>
{
  final Class<T> clazz;
  // as before
}

class HandlerDispatcher
{
  private final Map<Class<?>, RuleHandler<?>> handlers;
  void register(RuleHandler<?> handler)
  {
    handlers.put(handler.clazz, handler);
  }
  void doStuff(List<Rule> rules)
  {
    for(Rule rule : rules)
    {
      @SuppressWarnings("unchecked")
      RuleHandler<Rule> handler = (RuleHandler<Rule>) 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);
  }
}

答案 3 :(得分:0)

问题在于这一行。

RuleHandler<? extends Rule> handler = handlers.get(rule.getClass());

编译器不知道&lt;?扩展规则&gt;是正确的类,因为你查找了规则类的正确处理程序。您可以替换为

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

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

@SuppressWarnings("unchecked")