假设我在客户端对象上有不同的方法,我需要保留对它们的引用,作为对我的高阶函数的自变量:
// 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);
// ...
您可以使用非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
;而如果数组太短,则将抛出NullPointerException
。如果数组为null,则抛出该异常(第一种情况除外)。
如果您想让短数组简单地传递空值,类似于使用很少的参数调用时JavaScript函数参数undefined
的方式,那么you必须在lambda中为此编写代码,例如
myClass.invokeClientMethod((r, a) -> client.method3(r, (a.length > 0 ? a[0] : null),
(a.length > 1 ? a[1] : null)));
编译器无法为您做出该决定,这就是为什么you必须这样做,即为什么简单的方法引用还不够。