Mi clase DownloadTask se extiende javafx.concurrent.Task<Boolean>

Después de enviar mi tarea a un ExecutorService, trato de asignar el resultado a una variable de tipo Future<Boolean>. Sin embargo, el IDE me advierte que mi objeto instanciado tiene un tipo genérico <capture<?>> y no sé por qué.

Mi clase DownloadTask:

import javafx.concurrent.Task;

private class DownloadTask extends Task<Boolean>{
    public DownloadTask() {}

    @Override
    protected Boolean call() throws Exception {
         return true;
    }
}

Cómo administro el grupo de subprocesos:

ExecutorService executor = Executors.newFixedThreadPool(10);
ArrayList<Future<Boolean>> resultList = new ArrayList<>();

for (ScheduleRow scheduleRow : queArray) {
    DownloadTask downloadTask = new DownloadTask();

    Future<Boolean> result = executor.submit(downloadTask); //incompatible types

    resultList.add(result);
}

executor.shutdown();
1
Daniel H. 26 oct. 2017 a las 21:33

3 respuestas

La mejor respuesta

Respuesta corta

Para resolver esa advertencia, podría simplemente agregar información de tipo para habilitar la inferencia de tipo:

Future<Boolean> result = executor.submit(downloadTask, true);

Sin embargo, tenga en cuenta que, para la variable result, el método Future s get devolverá el verdadero dado cuando se complete con éxito, ya que DownloadTask se interpreta como un Runnable que no produce un resultado. Como dice Mike Strobel, una mejor manera es obtener el resultado correcto del DownloadTask mismo, ya que también es un Future<Boolean>:

executor.submit(downloadTask);
resultList.add(downloadTask);

Respuesta larga

Como dijo Andy Turner, el tinte heredado para javafx.concurrent.Task es Runnable (ya que extiende FutureTask implementando RunnableFuture).

Pasar esto al método Future<?> submit(Runnable task) del ExecutorService arrojará un resultado Future<?> y el IDE arrojará ese desagradable Future<capture<?>>.

Sin embargo, no necesita un ExecutorService específico de javafx, el java.util.concurrent.ExecutorService funciona bien, ya que DownloadTask es una subclase de FutureTask.

Para corregir la inferencia de tipos, puede usar el método genérico <T> Future<T> submit(Runnable task, T result) de java.util.concurrent.ExecutorService. Como es un booleano simple, puede confiar en el autoboxing:

Future<Boolean> result = executor.submit(downloadTask, true);

El problema es que, para la variable result, el método get de Future devolverá el verdadero dado cuando se complete con éxito. En su ejemplo, el método get en el Future resultante siempre devolvería null. Esto se debe a que se interpreta como un Runnable que no produce un resultado. Como dice Mike Strobel, una mejor manera es obtener el resultado correcto de DownloadTask, ya que también es un futuro:

executor.submit(downloadTask);
resultList.add(downloadTask);
1
Nick Vanderhoven 26 oct. 2017 a las 21:24

Realmente no necesitas hacer nada con el resultado de submit; su DownloadTask es ya a Future<Boolean>, y cuando se ejecute, establecerá su propio resultado. El Future<?> devuelto por submit no es capaz de reenviar el resultado de su tarea de todos modos, ya que simplemente lo ve como un Runnable que no produce un valor.

Simplemente cambie esto:

Future<Boolean> result = executor.submit(downloadTask);
resultList.add(result);

A esto:

executor.submit(downloadTask);
resultList.add(downloadTask);
0
Mike Strobel 26 oct. 2017 a las 20:58

De acuerdo con el Javadoc de javafx.concurrent.Task, Task implementa Runnable, no Callable<T>.

Como tal, cuando lo envía a un ExecutorService, se trata como algo que no devuelve un valor, por lo que el futuro resultante es un Future<?>.

Utilice un ExecutorService específico de javafx (no tengo idea si hay uno especialmente para Task); o haga que su clase implemente Callable<Boolean> en su lugar.

0
Andy Turner 26 oct. 2017 a las 18:46