【问题标题】:Can I create a functional interface that can store a method reference to methods containing any number of arguments?我可以创建一个函数式接口来存储对包含任意数量参数的方法的方法引用吗?
【发布时间】:2020-04-08 06:54:45
【问题描述】:

假设我在客户端对象上有不同的方法,我需要将它们的引用作为我端的高阶函数的参数:

// 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 对象,并在第一个Request 参数之后采用任意数量的String 参数。我希望客户能够在他们的类中以上述格式创建任何这些函数,然后将其方法引用作为参数传递给我的函数。

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

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

但这个函数式接口只能保留对本身是显式可变参数的方法的引用,如下所示:

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

根据设计,我不希望我的客户将他们的函数显式定义为可变参数方法 (String...),因为这会损害可读性。我可以创建一个函数式接口,该接口可以存储对包含任意数量参数的方法的方法引用本身不是可变参数函数吗?

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

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

【问题讨论】:

  • 您实际上打算如何调用您作为requestHandler 传递的方法,您将传递什么参数以及多少?您可能不得不使用反射而不是使用功能接口。您将失去所有类型安全,但那是因为在类型安全恕我直言时无法完成您想要实现的目标。
  • 未显示,但我实际上在invokeClientMethod 中有另一个参数,我可以推断出需要传递给requestHandler 的参数数量。我最初的计划是推断该信息,然后调用.args(部分)或.apply n 次。我不想完全使用反射,因为我想给我的客户某种形式的编译时安全性——如果我让我的客户将方法的完全限定名称作为字符串传递,我就没有办法了验证该方法是否在运行时之前确实存在...

标签: java functional-programming functional-interface


【解决方案1】:

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

你必须做映射,例如使用简单的 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 将如果数组为空则抛出(第一种情况除外)。

如果您希望短数组简单地传递空值,类似于 JavaScript 函数参数在使用太少的参数调用时的 undefined,那么 你 必须在 lambda 中编写代码,例如

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 2016-07-02
    • 1970-01-01
    • 1970-01-01
    • 2015-12-26
    相关资源
    最近更新 更多