invokeAll()不愿接受Collection<Callable<T>>。

7
我不明白为什么这段代码无法编译。
ExecutorService executor = new ScheduledThreadPoolExecutor(threads);

class DocFeeder implements Callable<Boolean> {....} 
... 
List<DocFeeder> list = new LinkedList<DocFeeder>();
list.add(new DocFeeder(1));
...
executor.invokeAll(list);

错误信息是:
The method invokeAll(Collection<Callable<T>>) in the type ExecutorService is 
not applicable for the arguments (List<DocFeeder>)  

listDocFeeder 的集合,实现了 Callable<Boolean> 接口 - 到底是什么意思?!

4个回答

18

稍微补充一下saua的答案:

在Java 5中,该方法声明为:

invokeAll(Collection<Callable<T>> tasks) 
在Java 6中,该方法声明为:

invokeAll(Collection<? extends Callable<T>> tasks) 

通配符的差异非常重要 - 因为 List<DocFeeder> Collection<? extends Callable<T>>,但它不是 Collection<Callable<T>>。考虑一下这个方法会发生什么:

public void addSomething(Collection<Callable<Boolean>> collection)
{
    collection.add(new SomeCallable<Boolean>());
}
那是合法的 - 但如果你能够使用 List 调用 addSomething 方法,这显然是有问题的,因为它会尝试将一个非 DocFeeder 对象添加到列表中。因此,如果您被困在 Java 5 中,则需要从 List 创建一个 List>。

7

这段代码在Java 6中可以完美编译,但在Java 5下编译失败,报错如下:

Foo.java:9: cannot find symbol
symbol  : method invokeAll(java.util.List)
location: interface java.util.concurrent.ExecutorService
executor.invokeAll(list);
        ^
1 error

list更改为以下内容后,问题得以解决:

Collection<Callable<Boolean>> list = new LinkedList<Callable<Boolean>>();

使其在Java 5和Java 6上都能正常工作。

0

感谢详细的回答,但是仍然让我困扰 - Callable是一个接口,所以实际上,Jon答案中的"addSomething"函数应该是可以的(不仅合法,而且合理)- 因为,嗯,这就是接口的全部意义 - 只要你遵守一些最初的协议,我不在乎你将哪个对象添加到列表中。在我看来,你提出的问题应该在不同的范围内解决。

除此之外,事实仍然存在,即代码无法编译 - 而它本应该能够编译...


不,它(针对Java 5)不应该编译通过——因为正是由于那个变异问题。基本上,泛型的变异并不按照你所期望的方式工作,也不应该这样——这将是危险的。 - Jon Skeet
当然,他们应该像Java 6中那样在Java 5中声明方法-那里应用的修复措施是完全适当的。 - Jon Skeet
1
这个修复方案不合适,因为它意味着你不能为1.5和1.6同时编写一个ExecutorService。 (jsr166_已经为这两个错误道歉了。) - Tom Hawtin - tackline
@Tom:没错,但我宁愿有一个在客户端兼容的修复,因为使用ExecutorService的人肯定比编写它的人更多。 - Joachim Sauer

0
Collection<Callable<Boolean>> list = new LinkedList<Callable<Boolean>>();

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