Подтвердить что ты не робот

Сборники и универсалы

Мне нужно запустить кучу задач в параллельных потоках и получить результаты.

Вот мой код:

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>).

Но мой вопрос: почему эта ошибка возникает? Я не понимаю, почему я не могу так писать. Кто-нибудь может это понять?

4b9b3361

Ответ 1

<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);

Это хорошо известная ошибка при разработке общих API-интерфейсов в Java. Отсутствие ковариации сайта объявления (что позволило бы утверждать, что 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();
}

если вам это нужно более одного раза, вы можете поместить его в метод утилиты, имея в виду использовать правильную подпись; -)

Ответ 2

Отклонение от ответа на @skiwi:

Ваш список List<Callable<? extends Object>>, который эффективно "List of Callable чего-то"

Что ищет invokeAll() - это Collection<? extends Callable<T>>

Итак, a List является Collection. Эта часть работает.

Однако, поскольку Java-генерики являются инвариантными, Callable чего-то делает не расширение Callable<T>, даже если T является Object, поскольку это может быть что угодно, а Callable ничего, кроме Object, будет не растягиваться Callable<Object>. Поэтому компилятор не имеет понятия, что делать.

Поэтому компилятор жалуется.

Обратите внимание, что даже если у вас есть List<Callable<? extends Integer>> и попытался позвонить <Integer> invokeAll(), это все равно не сработает. У вас будет Callable того, что расширяет Integer, а invokeAll() ищет что-то, что расширяет Callable<Integer>. И Callable<? extends Integer> не может быть доказано extend Callable<Integer>, так как родовые Java являются инвариантными.

Ну, это был плохой пример, так как Integer - это последний класс. Но вы понимаете.