使用泛型是否有解决我的问题的方法?

时间:2011-01-18 11:12:57

标签: java generics map

我正在使用java.util.concurrency框架实现一些代码。我将把一组callables传递给一个将并行执行它们的类。我正试图找出以类型安全的方式获取每个响应的最佳方法。这里有一些代码可以帮助解释我正在做的事情:

首先我创建我的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键的类型参数。我担心的是,如果有人重构工作单元的返回类型(例如从IntegerBigDecimal或其他),那么来自Object的强制转换将永远不会被自动重构工具和可能导致运行时问题。


结论:感谢下面的所有有用的评论和讨论,我采取了略微不同的机智(尽管上面提到的Sean Patrick Floyd对我的问题是正确的)。我最终完全删除了响应映射,并使用响应填充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对象。接下来,在我的并行处理中,我得到每个Future创建的响应并填充AbstractParallelCallable:

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

其中getCallableHandle返回AbstractParallelCallable对象,ParallelFutureTask通过提供对Callable对象的引用来包装FutureTask。执行后,调用代码可以执行此操作:

Integer theResult = firstCallable.getResponse();

4 个答案:

答案 0 :(得分:8)

基于方法的方法

你能做到的唯一方法是将它封装在一个方法中:

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 :(得分:1)

快速回答:不,你不能在Map中存储多种类型,不能没有它们共享相同的父类型(例如Object)。您也不希望能够这样做。我会考虑在这里重新思考你的设计。是否真的需要将您的回复放在Map对象中?我假设您很快再次从地图中检索值。

我可能会使用各种回调机制。一旦完成Callable,它就会在本地存储响应。当ParralelSender发送了all(假设这将一次性返回所有Callable的结果)时,它会遍历每个Callable并告诉已注册的侦听器处理结果。

不确定我是否有意义,顺便说一句。我试着想出一个例子,但是懒惰。

答案 2 :(得分:0)

如果您的所有回复都是一种类型,那么就有一个解决方案。

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

但是如果你在同一个地图中放置不同的结果类型,而你的代码取决于它们的实际类型,那么泛型将无法帮助你。

答案 3 :(得分:0)

这样的事情:

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'方法中进行强制转换。