如何让这个嵌套通用参数系统正常工作?

发布于 2024-10-06 20:28:58 字数 1245 浏览 4 评论 0原文

所以我正在努力让一个相当复杂的系统运行起来。这是我正在尝试的基础知识。

规则:

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());
  }
}

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

So I'm trying to get a reasonably complicated system working. Here's the basics of what I'm attempting.

Rules:

abstract class Rule
{
  // stuff
}

class ExampleRule extends Rule
{
  // stuff
}

Handlers:

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
  }
}

And tying them together:

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());
  }
}

So far I've attempted various combinations of different parameters (wildcarded, restricted, etc.) and have yet to get this compiling without type-related errors. Any insights, solutions or alternative approaches are welcome.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

花落人断肠 2024-10-13 20:28:58

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

在这里,您尝试通用且动态地处理事情。这一般是不可能的。使用原始类型并忍受类型不安全。

我自己,我想说你只是在自找麻烦:

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 上,以便为给定规则子类的子类找到处理程序)

You're trying to use generics in a runtime manner. If you don't know at compile time the types you need to handle, (be it a real type or a type parameter itself), you can't use Generics, plain and simple. It's a compile time only (or mostly) construct.

Here, you're trying to handle things both generically and dynamically. That is generally impossible. Use raw types and live with the type unsafety.

Myself, I'd say you're just asking for trouble with this:

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

Just make it this:

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

And then only register that rule handler with types it can handle.

Edit

Based on your comment it seems like your goal is to enforce your design on users of your interface. I stand by the argument that you should separate the concept of filtering and handling.

That said, another option is to leave the class token out of the handler itself and generify your register method.

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);
           }
       }
   }
}

Here, we suppress a warning when you use the raw type RuleHandler. We know that it is safe through inspection only, by looking at accesses to map and seeing that the class always matches the RuleHandler type parameter. However, this will obviously only be safe if there were no type safety warnings for the client when they invoked register() (i.e. they parameterized the call to register()).

(The inner for-loop was added over get such that a handler would be found for subclasses of a given Rule subclasses)

柠檬色的秋千 2024-10-13 20:28:58

你无法避免这里未经检查的强制转换。

对于某些类 Thandlers 映射中的每个条目都将 ClassRuleHandler 相关联> 每个条目都不同。 Map 的方法没有表达这个限制。

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

有关示例,请查看 GuavaClassToInstanceMap

You cannot avoid unchecked casts here.

Each entry in your handlers map relates a Class<T> to a RuleHandler<T>, for some class T that is different for each entry. The methods of Map do not express this restriction.

What you can do is create a subclass of Map that enforces type consistency per entry and perform all the unchecked casts in your new map class.

For an example, look Guava's ClassToInstanceMap.

别闹i 2024-10-13 20:28:58

观察到

  • 处理程序是私有的,并且最终的
  • handler.clazz 是最终的
  • RuleHandler意味着 RuleHandler === 规则处理程序

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

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);
  }
}

Observing that

  • handlers is private and final
  • handler.clazz is final
  • RuleHandler<T extends Rule> implies RuleHandler<?> === RuleHandler<? extends Rule>

the following code is correct (and compiles)

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);
  }
}
溺ぐ爱和你が 2024-10-13 20:28:58

问题就出在这一行。

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

编译器不知道 是正确的类,因为您查找了规则类的正确处理程序。您可以替换为

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

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

@SuppressWarnings("unchecked")

The problem is this line.

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

The compiler doesn't know that <? extends Rule> is the right class because you looked up the correct handler for the Rule's class. You can replace with

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

This will produce a warning because the compiler doesn't know that at runtime, you will pick the right type. If this bothers you, you can add to your class.

@SuppressWarnings("unchecked")
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文