我正在使用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
键的类型参数。我担心的是,如果有人重构工作单元的返回类型(例如从Integer
到BigDecimal
或其他),那么来自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();
答案 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'方法中进行强制转换。