我可以创建一个功能接口来存储对包含任意数量参数的方法的方法引用吗?

时间:2019-12-15 16:46:53

标签: java functional-programming functional-interface

假设在客户端对象上有不同的方法,我需要保留对这些对象的引用,作为对我的高阶函数的自变量:

// client class example methods
public Response method1(Request request);
public Response method2(Request request, String name);
public Response method3(Request request, String title, String description);

// my class
public Response invokeClientMethod(RequestHandler handler){
    // do something with method reference
}

// client code passes a reference to any of those methods as an argument to my function
myClass.invokeClientMethod(clientClass::method1);
myClass.invokeClientMethod(clientClass::method2);
myClass.invokeClientMethod(clientClass::method3);

这些方法始终将Request对象作为第一个参数,返回一个Response对象,并在第一个String参数之后采用任意数量的Request个参数。我希望客户端能够以上述格式在其类中创建任何这些函数,然后将其方法引用作为参数传递给我的函数。

因此,如果可能的话,我希望能够使用相同的功能接口保留对所有这些方法的引用。我尝试了以下方法:

@FunctionalInterface
public interface RequestHandler{
    Response apply(Request request, String... args);
}

但是此功能接口只能保留对本身是显式vararg参数的方法的引用,例如:

// client class
public Response method4(Request request, String... args);

通过设计,我不希望我的客户将其函数显式定义为vararg方法(String...),因为这会损害可读性。我可以创建一个函数接口来存储对方法的引用,该方法引用包含任意数量的自变量而不是vararg函数吗?

我也不希望最终做这样的事情:

@FunctionalInterface
public interface RequestHandlerWithNoArgs{
    Response apply(Request request);
}

@FunctionalInterface
public interface RequestHandlerWithOneArg{
    Response apply(Request request, String arg1);
}

@FunctionalInterface
public interface RequestHandlerWithTwoArgs{
    Response apply(Request request, String arg1, String arg2);
}

// my class
public Response invokeClientMethod(RequestHandlerWithNoArgs requestHandler);
public Response invokeClientMethod(RequestHandlerWithOneArg requestHandler);
public Response invokeClientMethod(RequestHandlerWithTwoArgs requestHandler);
// ...

1 个答案:

答案 0 :(得分:1)

您不能使用非varargs方法的方法引用来实现varargs方法,因为编译器不知道如何处理运行时传递的实际参数数量与所引用方法所需的预期参数数量之间的差异

必须进行映射,例如使用简单的lambda表达式,忽略差异:

myClass.invokeClientMethod((r, a) -> client.method1(r));
myClass.invokeClientMethod((r, a) -> client.method2(r, a[0]));
myClass.invokeClientMethod((r, a) -> client.method3(r, a[0], a[1]));

该代码忽略了a长度可能错误的事实,即,如果数组太大,则忽略任何多余的值;如果数组太短,则抛出ArrayIndexOutOfBoundsException,并且如果数组为null(第一种情况除外),将引发NullPointerException

如果您想让短数组简单地传递空值,类似于使用很少参数调用JavaScript函数参数undefined的方式,那么必须在其中编写代码Lambda,例如

myClass.invokeClientMethod((r, a) -> client.method3(r, (a.length > 0 ? a[0] : null),
                                                       (a.length > 1 ? a[1] : null)));

编译器无法为您做出决定,这就是为什么必须这样做,即为什么简单的方法引用还不够。