Есть ли решение моей проблемы с использованием дженериков? - PullRequest
3 голосов
/ 18 января 2011

Я реализую некоторый код, используя инфраструктуру java.util.concurrency. Я буду передавать коллекцию вызываемых объектов классу, который будет выполнять их параллельно. Я пытаюсь найти лучший способ получить каждый ответ безопасным способом. Вот некоторый код, чтобы помочь объяснить, что я делаю:

Сначала я создаю свои Callable s, которые являются единицами работы, которые должны вызываться параллельно. Например, здесь первая единица работы возвращает String, а вторая Integer:

Callable<String> firstCallable = new Callable<String>(){
   public String call() {...}
};

Callable<Integer> secondCallable = new Callable<Integer>(){
   public Integer call() {...}
};

Теперь я запускаю их в свою структуру, чтобы запустить их параллельно, и хитрость заключается в получении дескриптора к подходящему объекту ответа. Вот реализация, которая работает:

Map<Callable,Object> responseMap = ParallelSender.send(firstCallable, 
                                                       secondCallable);

, где Object - это ответ конкретного Callable. Таким образом, вы можете сделать это:

String firstCallableResponse = (String)responseMap.get(firstCallable);
Integer secondCallableResponse = (Integer)responseMap.get(secondCallable);

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

Map<Callable<T>, T> responseMap = ParallelSender.send(...);
String firstCallableResponse = responseMap.get(firstCallable);

так, что возвращаемое значение основано на набранном параметре клавиши Callable. Меня беспокоит то, что если кто-либо изменит рефакторинг типа возвращаемой единицы работы (скажем, от Integer до BigDecimal или чего-либо еще), то приведение от Object никогда не будет обнаружено автоматическими инструментами рефакторинга и может привести к проблемам во время выполнения.


ЗАКЛЮЧЕНИЕ: Благодаря всем полезным комментариям и обсуждению ниже, я выбрал немного другой такт (хотя Шон Патрик Флойд заслужил верность моему вопросу выше). В итоге я полностью сбросил карту ответов и заполнил объект Callable ответом. Вот соответствующие фрагменты кода:

public abstract class AbstractParallelCallable<V> implements Callable<V> {

   /** The response generated by the Call method of this class. */
   private V callableResponse;

   public V getResponse() {
       return callableResponse;
   }

   public void setResponse(V response) {
       callableResponse = response;
   }
}

Таким образом, у меня есть абстрактная реализация, которая упаковывает объект Callable, сохраняя ответ. Затем, в моей параллельной обработке я получаю ответ, созданный каждым Будущим, и заполняю AbstractParallelCallable:

for (ParallelFutureTask<Object> future : futures) {
   try {
      future.getCallableHandle().setResponse(future.get());
   } catch(Exception e) {...}
}

, где getCallableHandle возвращает объект AbstractParallelCallable и обертки ParallelFutureTask FutureTask, предоставляя ссылку на объект Callable. После выполнения вызывающий код может сделать это:

Integer theResult = firstCallable.getResponse();

Ответы [ 4 ]

8 голосов
/ 18 января 2011

Подход, основанный на методе

Единственный способ сделать это - это инкапсулировать его в метод:

class ParallelSender{

    private final Map<Callable<?>, Object> map =
        new HashMap<Callable<?>, Object>();

    @SuppressWarnings("unchecked")
    public <T> T getResult(final Callable<T> callable){
        return (T) map.get(callable);
    }

}

Код клиента

Теперь код вашего клиентане нужно кастовать:

ParallelSender parallelSender = new ParallelSender();
Callable<Integer> integerCallable = new Callable<Integer>(){

    @Override
    public Integer call() throws Exception{
        return Integer.valueOf(1);
    }
};
Integer result = parallelSender.getResult(integerCallable);
1 голос
/ 18 января 2011

Быстрый ответ: Нет, вы не можете хранить несколько типов на карте, если они не имеют общий родительский тип (например, объект). Вы также не хотите быть в состоянии сделать это. Я хотел бы пересмотреть ваш дизайн здесь. Действительно ли вам нужно поместить свои ответы в объект Map? Я предполагаю, что вы вскоре получите значения с карты снова.

Я бы, вероятно, использовал своего рода механизм обратного вызова. Как только Callable готов, он сохраняет ответ локально. Когда ParralelSender отправил все (при условии, что это вернет все результаты Callable за один раз), он перебирает каждый Callable и сообщает зарегистрированному слушателю обработать результат.

Не уверен, что я правильно понял, кстати. Я бы попробовал привести пример, но ленивый.

0 голосов
/ 18 января 2011

Примерно так:

Callable<String> firstCallable = new Callable<String>(){
   public String call() {...}
};

Callable<Integer> secondCallable = new Callable<Integer>(){
   public Integer call() {...}
};


class ParallelSender {
    ParallelResults send(Callable<Object> o,...) {...}
}

class Parallelresults {
    T <T> get(Callable<T> key) { ... }
}

ParallelResults res = ParallelSender.send(firstCallable, 
                                          secondCallable);

String s = res.get(firstCallable);
Integer i = res.get(secondCallable);

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

0 голосов
/ 18 января 2011

Если все ваши ответы относятся к одному типу, значит, есть решение.

Map<Callable<String>,String> responseMap = ParallelSender.send(firstCallable, 
                                                   secondCallable);
String firstCallableResponse = responseMap.get(firstCallable);

Но если вы помещаете разные типы результатов в одну и ту же карту, а ваш код зависит от их фактических типов, универсальные шаблоны вам не помогут.

...