Search code examples
javagenericsnested-generics

How do I get this system of nested generic parameters working?


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.


Solution

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