如何将一个函数作为谓词传递给另一个函数,当在运行时收到谓词函数的名称时?
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
也可以.
我收到要在运行时用作 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
也可以.