如何将一个函数作为谓词传递给另一个函数,当在运行时收到谓词函数的名称时?

How to pass a function as predicate to another function, When name of the predicate function is received at RunTime?

我收到要在运行时用作 biPredicate 的函数的名称。我想传递这个 biPredicate 并进行评估,基本上是过滤以获得结果。 以下是我定义 biPredicate 的实用程序。我尝试使用 MethodHandle 和 Lambda 函数。当我使用

new FilterUtility().execute("genericFilter");

I get java.lang.AbstractMethodError

public class FilterUtility {

public void execute(String filterName) throws Throwable {
    ActualBean beanObject = ActualBean.builder().param1("true").param2("false").build();

    MethodType methodType = MethodType.methodType(boolean.class, Object.class, Object.class);
    MethodHandles.Lookup lookup = MethodHandles.lookup();
    MethodHandle handle = lookup.findStatic(FilterUtility.class, filterName, methodType);
    BiPredicate<Object, Object> f = (BiPredicate<Object, Object>) LambdaMetafactory.metafactory(lookup,
            "test",
            MethodType.methodType(BiPredicate.class),
            methodType.generic(),
            handle,
            methodType)
            .getTarget()
            .invokeExact();

    resolve(beanObject, new HashMap<>(), f);
}

public static <SourceObject, TemplateObject> Map<String, String> resolve(SourceObject src,
        TemplateObject template,
        BiPredicate<SourceObject, TemplateObject> p) {
    if (p.test(src, template))
        return new HashMap<>();

    return null;
}

public static <SourceObject, TemplateObject> boolean genericFilter(SourceObject x, TemplateObject y) {
    ObjectMapper ob = new ObjectMapper();
    Map<String, Object> source = ob.convertValue(x, Map.class);
    Map<String, Object> template = ob.convertValue(y, Map.class);

    for (Map.Entry<String, Object> entry : template.entrySet()) {
        if (!source.get(entry.getKey()).equals(entry.getValue()))
            return false;
    }
    return true;
}
}

当我将 execute 的实现更改为 following 时,我没有得到异常。

public void execute(String filterName) throws Throwable {
    ActualBean beanObject = ActualBean.builder().param1("true").param2("false").build();
    resolve(beanObject, new HashMap<>(), FilterUtility::genericFilter); }

这让我相信我尝试查找具有名称的函数并将其作为 biPredicate 发送的方式有问题。

您正在调用方法 methodType.generic(),它将所有参数类型和 return 类型替换为 java.lang.Object,包括原始类型。因此,您正在将目标方法的签名 (Object,Object)->boolean 转换为 (Object,Object)->Object,有效地创建一个带有方法 Object test(Object,Object) 的 class,它将调用您的目标方法并将结果装箱。

lambda 元工厂不检查此类类型不匹配。因此,当您尝试在生成的 class 上调用 BiPredicate 的方法 boolean test(Object,Object) 时,将抛出错误。

正确的使用方法是 methodType.erase(),它将用 java.lang.Object 替换所有引用类型,但保持原始类型不变。但是,在这种特定情况下,您根本不需要转换方法类型,因为目标方法的类型已经是 (Object,Object)->boolean,所以只需将 methodType.generic() 替换为 methodType 也可以.