2

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

规则:

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

4 回答 4

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())时才是安全的。

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

于 2010-12-08T18:00:25.123 回答
1

你不能在这里避免未经检查的演员表。

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

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

例如,查看GuavaClassToInstanceMap

于 2010-12-08T18:00:26.387 回答
1

观察到

  • handlers 是私有的和最终的
  • 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);
  }
}
于 2010-12-09T11:39:08.033 回答
0

问题是这条线。

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

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

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

这将产生一个警告,因为编译器不知道在运行时,您将选择正确的类型。如果这让您感到困扰,您可以添加到您的课程中。

@SuppressWarnings("unchecked")
于 2010-12-08T17:54:54.077 回答