Java Lambda 表达式和对泛型方法的方法引用

sto*_*r96 5 java generics lambda functional-interface method-reference

我有一个功能界面

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

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

和两个函数

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) {
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

我想SubmitterCompletable使用 lambda 表达式或方法引用从这些函数创建 s。第一个通过使用方法引用工作得很好。

SubmitterCompletable submitterCompletable = CompletableFutureUtils::runAsync;
Run Code Online (Sandbox Code Playgroud)

然而,对于第二个,我必须使用 lambda 表达式来传递 an Executor,但它不起作用。

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

我的问题是这种情况下是否有有效的 lambda 表达式,或者在这种情况下我是否必须创建一个匿名类?

dsh*_*lya 1

这里的问题是“只有当函数接口中的方法没有类型参数时,lambda 表达式才可以用于函数接口”。(JLS11,15.27.3 Lambda 表达式的类型),但有一个例外 -全等方法引用并非如此。

这就是为什么它在第一个示例中有效而在第二个示例中无效的原因:

SubmitterCompletable submitterCompletable = CompletableFutureUtils::runAsync; (OK)
SubmitterCompletable submitterCompletable = c -> <anything> (NOT OK)
Run Code Online (Sandbox Code Playgroud)

我能想到的实现你想要的东西的选择并不多:

  1. 实现接口(如您所提到的就地使用匿名类或作为独立类)。

  2. 在您的内部使用一个中间帮助程序类,CompletableFutureUtils该类将保留对执行程序的引用,并公开与您的提交者的功能方法一致的方法,该方法会将调用委托给底层runAsync(Callable<U> callable, Executor executor)util 的方法。

示例代码:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

SubmitterCompletable submitterCompletable = CompletableFutureUtils::runAsync; 
SubmitterCompletable submitterWithExecutor = CompletableFutureUtils.using(executor)::runAsync;
Run Code Online (Sandbox Code Playgroud)