Java: Как избежать ошибки «метод не применим для аргументов» - PullRequest
0 голосов
/ 26 июня 2019

Я понимаю, что этот тип вопроса задавался бесчисленное количество раз, но я не нашел ответа, который, кажется, адресован и / или объясняет, что 1) я делаю неправильно и 2) что мне нужно сделать, чтобы получить его правый.

У меня есть приложение, в котором я пытаюсь управлять коллекцией очередей. Пользователи диспетчера очереди будут запрашивать очередь, предоставляя уникальный идентификатор, и менеджер вернет очередь, если она уже управляется, или создаст новую очередь (посредством отражения) и вернет ее, если она еще не была создана.

Я закодировал менеджер очередей для использования обобщений Java и столкнулся с проблемой, которую я не понимаю и не могу решить, как ее исправить.

Вот менеджер очередей:

public class QueueManager<T extends MessageType, C extends BlockingQueue<T>> {

  private Map<UUID, C> queueMap;

  public void removeQueue(UUID id) {
    queueMap.remove(id);
  }

  public C getQueue(Class<C> clazz, UUID id) {
    if (!queueMap.containsKey(id)) {
      queueMap.put(id, constructQueue(clazz));
    }

    return queueMap.get(id);
  }

  private C constructQueue(Class<C> clazz) {
    C result = null;

    try {
      Constructor<C> constructor = clazz.getDeclaredConstructor();
      result = constructor.newInstance();
    } catch (NoSuchMethodException | SecurityException | InstantiationException
        | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
      // handle exception
    }

    return result;
  }

}

Код, который использует администратор очередей:

public class QueueingListener<T extends MessageType> implements MessageListener<T> {

  private QueueManager<T, BlockingQueue<T>> queueMgr = new QueueManager<>();
...
  @Override
  public void handleMessage(T message) {
...
      queueMgr.getQueue(LinkedBlockingQueue.class,
          message.getMessageHeader().getUUID());
...
  }

Код жалуется на ошибку компиляции:

The method getQueue(Class<BlockingQueue<T>>, UUID) in the type QueueManager<T,BlockingQueue<T>> is not applicable for the arguments (Class<LinkedBlockingQueue>, UUID)

Может кто-нибудь объяснить, что здесь не так и что мне нужно делать вместо этого?

Мне бы очень хотелось узнать, где найти действительно хороший учебник по дженерикам Java. Я посмотрел на многие источники, но они, кажется, охватывают только основы, чтобы заставить кого-то работать, и не охватывают более продвинутые концепции и более тонкие детали.

Ответы [ 2 ]

1 голос
/ 26 июня 2019

Я суммирую важные части этого кода для пункта, который я собираюсь сделать:

public class QueueManager<T extends MessageType, C extends BlockingQueue<T>> {
  public C getQueue(Class<C> clazz, UUID id) {
    if (!queueMap.containsKey(id)) {
      queueMap.put(id, constructQueue(clazz));
    }

    return queueMap.get(id);
  }
}

Обратите внимание, что C может быть любым классом, расширяющим BlockingQueue при первоначальном создании экземпляра этого класса. Однако, как только вы создаете этот объект, C устанавливается как некоторый тип определенного класса. Позже вы позвоните по следующему коду:

private QueueManager<T, BlockingQueue<T>> queueMgr = new QueueManager<>();

Строка выше определяет C как 'BlockingQueue'.

queueMgr.getQueue(LinkedBlockingQueue.class, message.getMessageHeader().getUUID());

Однако в этой строке вы переходите в совершенно другой объект. LinkedBlockingQueue реализует BlockingQueue, да, но объекты Class для обоих соответственно совершенно разные, и они не , указанные в описании вашего класса. То, что вы ищете, это класс чего-то, что расширяет C в сигнатуре вашего метода. Таким образом, исправление этой ошибки может состоять в том, чтобы изменить ваш метод getQueue () следующим образом:

  public <X extends C> C getQueue(Class<X> clazz, UUID id) {
    if (!queueMap.containsKey(id)) {
      queueMap.put(id, constructQueue(clazz));
    }

    return queueMap.get(id);
  }

Здесь есть тонкое, но важное синтаксическое различие, которое, я надеюсь, я объяснил ясно. Вы также изменили бы constructQueue () аналогичным образом. :)

1 голос
/ 26 июня 2019

Зачем заботиться о типе управляемых очередей, и почему бы просто не передать Supplier<BlockingQueue> получателю?

public class QueueManager<T extends MessageType> {

    private Map<UUID, BlockingQueue<T>> queueMap;

    public void removeQueue(UUID id) {
      queueMap.remove(id);
    }

    public BlockingQueue<T> getQueue(UUID id, Supplier<BlockingQueue<T>> createQueue) {
        return queueMap.computeIfAbsent(id, k -> createQueue.get());
    }

    // let's add a default while we're at it 
    public BlockingQueue<T> getQueue(UUID id) {
        return getQueue(id, () -> new ArrayBlockingQueue<T>(50));
    }
}

Это сделало бы вызов

queueMgr.getQueue(message.getMessageHeader().getUUID(), LinkedBlockingQueue::new);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...