Tengo una interfaz funcional.
import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; @FunctionalInterface public interface SubmitterCompletable extends Submitter { @Override <T> CompletableFuture<T> submit(Callable<T> task); }y dos funciones
import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; public final class CompletableFutureUtils { public static <U> CompletableFuture<U> runAsync(Callable<U> callable) { // ... } public static <U> CompletableFuture<U> runAsync(Callable<U> callable, Executor executor) { // ... } } y quiero crear SubmitterCompletable s a partir de estas funciones usando expresiones lambda o referencias de métodos. El primero funciona bien usando referencias de métodos.
SubmitterCompletable submitterCompletable = CompletableFutureUtils::runAsync; Sin embargo, para el segundo, tengo que usar una expresión lambda para pasar un Executor y no funciona.
Executor executor = /* ... */; SubmitterCompletable submitterCompletable = c -> CompletableFutureUtils.runAsync(c, executor); // Illegal lambda expression: Method submit of type SubmitterCompletable is genericMi pregunta es si hay una expresión lambda válida para este caso o tengo que crear una clase anónima en este caso.
Hay una expresión lambda válida, sin embargo, debe mover el tipo genérico en SubmitterCompletable del método al nivel de clase siempre que la expresión lambda no se pueda construir si el tipo genérico está definido en el método:
@FunctionalInterface public interface SubmitterCompletable<T> extends Submitter { // notice the <T> @Override CompletableFuture<T> submit(Callable<T> task); // reuses the generic type } Executor executor = /* ... */; SubmitterCompletable<Whatever> submitterCompletable = c -> runAsync(c, executor);El problema es que "una expresión lambda se puede usar para una interfaz funcional solo si el método en la interfaz funcional NO tiene parámetros de tipo". (JLS11, 15.27.3 Tipo de una expresión Lambda ) con una excepción: este no es el caso para las referencias de métodos congruentes .
Es por eso que funciona en su primer ejemplo y no en el segundo:
SubmitterCompletable submitterCompletable = CompletableFutureUtils::runAsync; (OK) SubmitterCompletable submitterCompletable = c -> <anything> (NOT OK)No hay muchas opciones que se me ocurran para lograr lo que quieres:
Implemente la interfaz (ya sea en el lugar usando una clase anónima como mencionó o como una clase independiente).
Use una clase auxiliar intermedia dentro de su CompletableFutureUtils que mantendría una referencia al ejecutor y expondría un método congruente con el método funcional de su remitente que delegará la llamada al método de utilidad subyacente runAsync(Callable<U> callable, Executor executor) .
Código de ejemplo:
public final static class CompletableFutureUtils { public static <U> CompletableFuture<U> runAsync(Callable<U> callable) { ... } public static <U> CompletableFuture<U> runAsync(Callable<U> callable, Executor executor) { ... } public static ExecutorRunnerProxy using(Executor executor) { return new ExecutorRunnerProxy(executor); } public static final class ExecutorRunnerProxy { private final Executor executor; private ExecutorRunnerProxy(Executor executor) { this.executor = executor; } public <T> CompletableFuture<T> runAsync(Callable<T> task) { return CompletableFutureUtils.runAsync(task, executor); } } }Ejemplo de uso:
SubmitterCompletable submitterCompletable = CompletableFutureUtils::runAsync; SubmitterCompletable submitterWithExecutor = CompletableFutureUtils.using(executor)::runAsync;