7

I fail to understand why this code won't compile

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 error msg is:

The method invokeAll(Collection<Callable<T>>) in the type ExecutorService is 
not applicable for the arguments (List<DocFeeder>)  

list is a Collection of DocFeeder, which implements Callable<Boolean> - What is going on?!

Jesse
  • 8,605
  • 7
  • 47
  • 57
Yossale
  • 14,165
  • 22
  • 82
  • 109

4 Answers4

18

Just to expand on saua's answer a little...

In Java 5, the method was declared as:

invokeAll(Collection<Callable<T>> tasks) 

In Java 6, the method is declared as:

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

The wildcarding difference is very important - because List<DocFeeder> is a Collection<? extends Callable<T>> but it's not a Collection<Callable<T>>. Consider what would happen with this method:

public void addSomething(Collection<Callable<Boolean>> collection)
{
    collection.add(new SomeCallable<Boolean>());
}

That's legal - but it's clearly bad if you can call addSomething with a List<DocFeeder> as it will try to add a non-DocFeeder to the list.

So, if you are stuck with Java 5, you need to create a List<Callable<Boolean>> from your List<DocFeeder>.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
7

That code compiles perfectly fine with Java 6, but fails to compile with Java 5 giving

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

However changing the list like this:

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

Makes it work on both Java 5 and Java 6.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
0

Thanks for the detailed answer , but it still bugs me - Callable is an interface , so actually , the "addSomething" function in Jon's answer should be OK (not only legal , but reasonable) - because , well , that's the whole point of interfaces - As long as you comply with some initial agreement , I don't care which object you'd add to the list. imo , the problem you presented should be addressed on a different scope.

Besides that , the fact remains that the code didn't compile - and it should have...

Yossale
  • 14,165
  • 22
  • 82
  • 109
  • No, it *shouldn't* have compiled (against Java 5) - because of that very variance problem. Basically variance in generics doesn't work the way you expect it to, and shouldn't - it would be dangerous. – Jon Skeet Dec 16 '08 at 09:45
  • (Of course, they should have declared the method in Java 5 the way they did in Java 6 - the fix applied there is entirely appropriate.) – Jon Skeet Dec 16 '08 at 09:46
  • 1
    The fix is inappropriate because it means you can't cleanly write an ExecutorService for both 1.5 and 1.6. (jsr166_ has appologised for both cockups.) – Tom Hawtin - tackline Dec 16 '08 at 14:17
  • @Tom: That's true, but I'd rather have a fix that's compatible on the client side, because there are definitely more people using an ExecutorService than there are people writing it. – Joachim Sauer Dec 17 '08 at 08:17
0
Collection<Callable<Boolean>> list = new LinkedList<Callable<Boolean>>();
laalto
  • 150,114
  • 66
  • 286
  • 303