我有几个类,每个类都实现一个接口。我从这些类中搜索出一种使用注释的方法。此方法返回一个布尔值,并且始终有一个对象作为参数,该对象始终继承自另一个固定对象。现在我想用这个方法创建一个功能接口。最佳当然是 Predicate,它接管上述参数。我现在已经尝试了一段时间来使用 LambdaMetafactory 来实现:
private Predicate<Parent> toPredicate(final ExampleInterface instance, final Method method) {
try {
final MethodHandle handle = LOOKUP.unreflect(method);
final MethodType signature = MethodType.methodType(boolean.class, method.getParameters()[0].getType());
final CallSite callSite = LambdaMetafactory.metafactory(
LOOKUP,
"test",
MethodType.methodType(Predicate.class, instance.getClass()),
signature,
handle,
signature
);
return (Predicate<Parent>) callSite.getTarget().invoke(instance);
} catch (Throwable e) {
e.printStackTrace();
return null;
}
}
我现在的问题是,当我调用 Predicate 的测试方法时,会抛出一个 AbstractMethodError。当我将签名与 (boolean.class, Parent.class) 一起使用时,我得到一个 LambdaConversionException。甚至可以动态地实现它吗?如果是,如何?
因为你知道目标接口类型是
Predicate
你也可以使用 lambda 表达式:
private Predicate<Parent> toPredicate(final ExampleInterface instance, final Method method) {
final MethodHandle handle = LOOKUP.unreflect(method)
.bindTo(instance)
.asType(MethodType.methodType(boolean.class, Parent.class);
return parent -> {
try {
return (boolean) handle.invokeExact(parent);
} catch (Throwable t) {
throw new RuntimeException("Should not happen", t);
}
};
}
只有在特定调用站点只能看到接口的 1 或 2 个实现的特定情况下,使用 LambdaMetafactory 才是真正有益的。另请注意,每次创建元工厂时,都会生成一个新类(除非它可以从 CDS 存档中加载),它有自己的相关成本。这些类与定义的类加载器紧密相关,例如如果这是应用程序类加载器,这些类实际上永远不会被卸载,如果
metafactory
被调用很多,这可能会导致元空间耗尽。
你弄错的是接口方法类型。应该是
test
中Predicate
方法的擦除:
final MethodHandle handle = LOOKUP.unreflect(method);
final CallSite callSite = LambdaMetafactory.metafactory(
LOOKUP,
"test",
MethodType.methodType(Predicate.class, instance.getClass()),
MethodType.methodType(boolean.class, Object.class),
handle,
MethodType.methodType(boolean.class, Parent.class)
);