可调用和generics的集合
我需要在并发线程中启动一堆任务并检索其结果。
这是我的代码:
List<Callable> tasks = new ArrayList(); // Adding some tasks whith return different types of results: // Callable, Callable, Callable, and so on... List<Future> results = executor.invokeAll( tasks );
但IDE显示下一个错误:
no suitable method found for invokeAll(List<Callable>) method ExecutorService.invokeAll(Collection<? extends Callable>) is not applicable (cannot infer type-variable(s) T#1 (argument mismatch; List<Callable> cannot be converted to Collection<? extends Callable> method ExecutorService.invokeAll(Collection<? extends Callable>,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 invokeAll(Collection<? extends Callable>) T#2 extends Object declared in method invokeAll(Collection<? extends Callable>,long,TimeUnit)
方法签名是:
List<Future> invokeAll(Collection<? extends Callable> tasks)
显然,我可以替换 使用
,并使我的所有任务都返回
Object
(例如,用SomeTask1 implements Callable
替换SomeTask1 implements Callable
SomeTask1 implements Callable
)。
但我的问题是:为什么会出现这种错误? 我不明白为什么我不能这样编码。 有人能说清楚吗?
List> invokeAll(Collection extends Callable > tasks)
这表示有一个类型变量T
,使得参数是Collection extends Callable
Collection extends Callable
。 也就是说,此方法签名假定列表中的所有Callables都具有相同的类型参数。 您的列表不是这种情况,这就是编译器拒绝您的代码的原因。
api方法应该声明如下:
List> invokeAll(Collection extends Callable extends T>> tasks);
在Java中设计通用API时,这是一个众所周知的陷阱。 缺少声明站点协方差(这将允许人们声明Callable
是Callable
的子类型)需要在每次使用generics类型时指定与通配符类型的协方差。 也就是说,API永远不应该写Callable
,但总是Callable extends T>
Callable extends T>
。 当然,正如这个例子所示,这是多余的,容易忘记。
在您的特定情况下,最好这样做:
List> futures = new ArrayList<>(); for (Callable> callable : tasks) { futures.add(executor.submit(callable)); } for (Future> future : futures) { future.get(); }
如果你不止一次需要这个,你可以把它放到一个实用工具方法中,记住使用正确的签名;-)
对@skiwi的回答进行扩展:
你的清单是List
List
,这实际上是“ Callable
的List
”
invokeAll()
正在寻找的是Collection extends Callable
Collection extends Callable
所以List
是一个Collection
。 那部分有效。
但是,因为Java的generics是不变的,所以Callable
of something不会扩展Callable
, 即使T
是Object
,因为它可以是任何东西 ,并且除了Object
之外的任何Callable
都不会扩展Callable
。 所以编译器不知道要推断什么。
所以编译器抱怨。
请注意,即使你有List
List
并尝试调用
,这仍然无效。 你会有一个Callable
扩展的东西的Callable
,而invokeAll()
正在寻找扩展Callable
东西 。 Callable extends Integer>
Callable extends Integer>
无法certificate可以extend Callable
,因为Java的generics是不变的。
嗯,这是一个不好的例子,因为Integer
是最后一堂课。 但是你明白了。