如何使用泛型正确地获取语句

3

我正在使用ExecutorService,并希望生成可调用对象,以便可以使用invokeAll执行它们。

这些可调用对象具有不同的返回类型。这让我想到可以使用通配符来完成此操作。

Set<Callable<?>> executedCallables = new HashSet<Callable<?>>();
executedCallables.add(serviceHelper.getOwnerDetails()); --> returns Callable<OwnerDetails>
executedCallables.add(iqmServiceHelper.getUserDetails()); --> returns Callable<UserDetails>

类似的方式,我添加了一个用于调用所有任务的语句

List<Future<? extends Object>> futures = executorService.invokeAll(executedCallables);

这段代码给我编译错误:The method invokeAll(Collection<? extends Callable<T>>) in the type ExecutorService is not applicable for the arguments (Set<Callable<?>>),我不知道该如何解决。
请问有人能指出使用错误并提供正确用法吗?
需要注意的是,这个编译错误来自JDK 6,并且我认为在更高版本的JDK中也没有什么区别。
PS:关于这个问题,StackOverflow上也有一个类似的讨论:Collection of Callable and Generics
1个回答

2
问题在于invokeAll方法的签名有点过于严格了。它应该是Collection<? extends Callable<? extends T>>,因为这里的T是一个生产者(从Effective Java中记住PECS - Producer Extends Consumer Super)。然而,我们当然不能在这里更改JDK方法,所以我们必须接受它。解决方案是传入一个Set<Callable<Object>>,并使用不安全的强制转换(这是安全的,因为你仅从Callable中提取T类型的值),或使用方法引用:
Set<Callable<Object>> callables = new HashSet<>();
callables.add((Callable) serviceHelper.getOwnerDetails()); // method one
callables.add(iqmServiceHelper.getUserDetails()::call); // method two

最终的语句将如下所示。
try {
    List<Future<Object>> futures = executorService.invokeAll(executedCallables);
} catch (InterruptedException e) {
    e.printStackTrace();
}

感谢您的回复。虽然上述语句构造良好,但我仍然不知道如何纠正最终语句List<Future<? extends Object>> futures = executorService.invokeAll(executedCallables)。 - Acewin
1
你将得到一个 List<Future<Object>>,而不是 List<Future<? extends Object>>。 在这里无法将 T 设为通配符。 - diesieben07
这段代码在我的机器上可以编译通过:Callable foo = () -> ""; Set> set = new HashSet<>(); set.add(foo::call); ExecutorService service = null; // 在这里填写适当的内容 List> futures = service.invokeAll(set); - diesieben07
1
我意识到我没有检查未处理异常的最终错误。已将其标记为正确解决方案。 - Acewin
总是那些小事情:D - diesieben07

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接