【问题标题】:How to pass a function as predicate to another function, When name of the predicate function is received at RunTime?如何将一个函数作为谓词传递给另一个函数,当在运行时收到谓词函数的名称时?
【发布时间】:2018-05-02 17:03:30
【问题描述】:

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

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

我得到 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;
}
}

当我将执行的实现更改为以下时,我没有收到异常。

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

这让我相信我试图找到具有名称的函数并将其作为双向谓词发送的方式有问题。

【问题讨论】:

  • 一旦我在这里创建方法句柄MethodHandle handle = lookup.findStatic(FilterUtility.class, filterName, methodType);,我使用调试器停止,然后我执行handle.invoke(order, new HashMap&lt;String, String&gt;()); 它给了我java.lang.UnsupportedOperationException。消息:MethodHandle.invoke 不能被反射调用
  • 删除第 12 行中的.generic()

标签: java reflection lambda functional-programming functional-interface


【解决方案1】:

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

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

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

【讨论】:

  • 我自己怎么能找到这个问题?也许调试器中的一些技巧你也可以分享我可以找到这些信息的资源吗?考虑到我会遇到更多问题,如果我已经知道去哪里寻找这些问题很容易解决。
  • 我认为,class documentation of LambdaMetafactory 是安静全面的。由于它主要面向专家(或者更确切地说,用于生成的代码,由编译器或专家制作的工具生成),因此它不会牺牲检查所有属性的性能。
  • 恐怕要完全理解所有细节(例如类型擦除的含义),了解The Java® Language SpecificationThe Java® Virtual Machine Specification 是不可避免的。
猜你喜欢
  • 2012-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-20
  • 2018-08-19
  • 1970-01-01
  • 1970-01-01
  • 2014-05-20
相关资源
最近更新 更多