简体   繁体   中英

Java Lambda Expressions and Method References to Generic Methods

I have a functional interface

import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;

@FunctionalInterface
public interface SubmitterCompletable extends Submitter {
    @Override
    <T> CompletableFuture<T> submit(Callable<T> task);
}

and two functions

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) {
        // ...
    }
}

and I want to create SubmitterCompletable s from these functions using lambda expressions or method references. The first one works fine by using method references.

SubmitterCompletable submitterCompletable = CompletableFutureUtils::runAsync;

For the second one, however, I have to use a lambda expression to pass an Executor and it doesn't work.

Executor executor = /* ... */;
SubmitterCompletable submitterCompletable = c -> CompletableFutureUtils.runAsync(c, executor);
// Illegal lambda expression: Method submit of type SubmitterCompletable is generic

My question is whether there is a valid lambda expression for this case, or do I have to create an anonymous class in this case?

There is a valid lambda expression, however, you need to move the generic type in SubmitterCompletable from the method to the class level as long as lambda expression cannot be constructed if the generic type is defined at the method:

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

The issue there is that "a lambda expression can be used for a functional interface only if the method in the functional interface has NO type parameters". (JLS11, 15.27.3 Type of a Lambda Expression ) with one exception - this is not the case for congruent method references.

That is why it works in your first example and doesn't in the second:

SubmitterCompletable submitterCompletable = CompletableFutureUtils::runAsync; (OK)
SubmitterCompletable submitterCompletable = c -> <anything> (NOT OK)

There aren't many options out there I could think of to achieve what you want:

  1. Implement the interface (either in-place using an anonymous class as you've mentioned or as a standalone class).

  2. Use an intermediate helper class inside your CompletableFutureUtils that would keep a ref to the executor and expose a method congruent with your Submitter's functional method which will delegate the call to the underlying runAsync(Callable<U> callable, Executor executor) util's method.

Example code:

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

Example usage:

SubmitterCompletable submitterCompletable = CompletableFutureUtils::runAsync; 
SubmitterCompletable submitterWithExecutor = CompletableFutureUtils.using(executor)::runAsync;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM