可调用和泛型的集合

时间:2014-05-21 11:00:36

标签: java generics

我需要在并发线程中启动一堆任务并检索其结果。

这是我的代码:

List<Callable<? extends Object>> tasks = new ArrayList<>();

// Adding some tasks whith return different types of results:
// Callable<Double>, Callable<String>, Callable<SomeOtherType>, and so on...

List<Future<? extends Object>> results = executor.invokeAll( tasks );

但IDE显示下一个错误:

no suitable method found for invokeAll(List<Callable<? extends Object>>)

    method ExecutorService.<T#1>invokeAll(Collection<? extends Callable<T#1>>)
            is not applicable
      (cannot infer type-variable(s) T#1
        (argument mismatch; List<Callable<? extends Object>> cannot be converted
                to Collection<? extends Callable<T#1>>

    method ExecutorService.<T#2>invokeAll(Collection<? extends Callable<T#2>>,long,TimeUnit)
            is not applicable
      (cannot infer type-variable(s) T#2
        (actual and formal argument lists differ in length))

  where T#1,T#2 are type-variables:
    T#1 extends Object declared in method
            <T#1>invokeAll(Collection<? extends Callable<T#1>>)
    T#2 extends Object declared in method
            <T#2>invokeAll(Collection<? extends Callable<T#2>>,long,TimeUnit)

方法签名是:

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

显然,我可以将<? extends Object>替换为<Object>,并使我的所有任务都返回Object(例如将SomeTask1 implements Callable<Double>替换为SomeTask1 implements Callable<Object>)。

但我的问题是:为什么会出现这种错误?我不明白为什么我不能这样编码。有人能说清楚吗?

2 个答案:

答案 0 :(得分:8)

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

这表示存在类型变量T,使得参数为Collection<? extends Callable<T>>。也就是说,此方法签名假定列表中的所有Callables都具有相同的类型参数。您的列表不是这种情况,这就是编译器拒绝您的代码的原因。

api方法应该声明如下:

<T> List<Future<? extends T>> invokeAll(Collection<? extends Callable<? extends T>> tasks);

在Java中设计通用API时,这是一个众所周知的陷阱。缺少声明站点协方差(这将允许人们声明Callable<String>Callable<Object>的子类型)需要在每次使用泛型类型时指定与通配符类型的协方差。也就是说,API永远不应该写Callable<T>,而应始终Callable<? extends T>。当然,正如这个例子所示,这是多余的,容易忘记。

在您的特定情况下,最好这样做:

List<Future<?>> futures = new ArrayList<>();
for (Callable<?> callable : tasks) {
     futures.add(executor.submit(callable));
}
for (Future<?> future : futures) {
    future.get();
}

如果你不止一次需要这个,你可以把它放到一个实用工具方法中,记住使用正确的签名; - )

答案 1 :(得分:1)

扩展@skiwi的答案:

您的列表为List<Callable<? extends Object>>,该列表实际上是&{34; List的{​​{1>} &#34;

Callable寻找的是invokeAll()

因此Collection<? extends Callable<T>>List。那部分有效。

但是,由于Java的泛型是不变的, Collection 扩展Callable即使Callable<T>T ,因为某些内容可以是任何,而Object除了{{}之外的任何内容1}}将扩展Callable。所以编译器不知道要推断什么。

所以编译器抱怨。

请注意,即使您有Object并试图致电Callable<Object>,这仍然无效。您将{{1>} 某些扩展List<Callable<? extends Integer>>,而<Integer> invokeAll()正在寻找扩展Callable某些 。并且Integer无法向invokeAll()证明,因为Java的泛型是不变的。

嗯,这是一个不好的例子,因为Callable<Integer>是最后一堂课。但你明白了。