I receive the name of the function to be used as biPredicate at runtime. I want to pass around this biPredicate and evaluate, basically filter to get results. Following is my utility that defines biPredicates. I tried utilizing MethodHandle and Lambda Functions. When I use
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;
}
}
When I change implementation of execute to following, I dont get exceptions.
public void execute(String filterName) throws Throwable {
ActualBean beanObject = ActualBean.builder().param1("true").param2("false").build();
resolve(beanObject, new HashMap<>(), FilterUtility::genericFilter); }
This makes me believe that there is something wrong with the way I am trying to find the function with name and send it as a biPredicate.
You are calling the method methodType.generic()
which will replace all parameter types and the return type with java.lang.Object
, including primitive types. So you are transforming the target method’s signature (Object,Object)->boolean
to (Object,Object)->Object
, effectively creating a class with a method Object test(Object,Object)
which will invoke your target method and box the result.
Such type mismatches are not checked by the lambda metafactory. So when you try to invoke BiPredicate
’s method boolean test(Object,Object)
on that generated class, an error will be thrown.
The correct method to use is methodType.erase()
, which will replace all reference types with java.lang.Object
but keep primitive types as-is. Though, in this specific case, you don’t need to transform the method type at all, as the target method’s type is already (Object,Object)->boolean
, so just replacing methodType.generic()
with methodType
would work too.