Как мне заставить работать эту систему вложенных общих параметров? - PullRequest
2 голосов
/ 08 декабря 2010

Итак, я пытаюсь заставить работать достаточно сложную систему. Вот основы того, что я пытаюсь сделать.

Правила:

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 голосов
/ 08 декабря 2010

Вы пытаетесь использовать дженерики во время выполнения. Если вы не знаете во время компиляции типы, которые вам нужно обрабатывать (будь то реальный тип или сам параметр типа), вы не можете использовать общие, простые и понятные. Это только конструкция времени компиляции (или в основном).

Здесь вы пытаетесь обрабатывать вещи как обобщенно, так и динамически. Это вообще невозможно. Используйте сырые типы и живите с типом unsafety.

Я бы сказал, что вы просто напрашиваетесь на неприятности с этим:

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, так что для подклассов данного подкласса правила будет найден обработчик)

1 голос
/ 09 декабря 2010

Замечание, что обработчики

  • являются частными и окончательными1012 * следующий код является правильным (и компилируется)
    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);
      }
    }
    
1 голос
/ 08 декабря 2010

Вы не можете избежать непроверенных приведений здесь.

Каждая запись в вашей карте handlers соотносит Class<T> с RuleHandler<T>, для некоторого класса T, который отличается для каждой записи.Методы Map не выражают это ограничение.

Что вы можете сделать, это создать подкласс Map, который обеспечивает согласованность типов для каждой записи и выполнять все непроверенные приведения в вашем новом классе карты.

Например, посмотрите Guava ClassToInstanceMap .

0 голосов
/ 08 декабря 2010

Проблема в этой строке.

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

Компилятор не знает, чтоправильный класс, потому что вы искали правильный обработчик для класса правила.Вы можете заменить на

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

Это выдаст предупреждение, поскольку компилятор не знает, что во время выполнения вы выберете правильный тип.Если это вас беспокоит, вы можете добавить в свой класс.

@SuppressWarnings("unchecked")
...