我正在尝试使用Java的LambdaMetaFactory
动态实现通用lambda,Handler<RoutingContext>
:
public class RoutingContext {
// ...
}
@FunctionalInterface
public interface Handler<X> {
public void handle(X arg);
}
public class HomeHandler extends Handler<RoutingContext> {
@Override
public void handle(RoutingContext ctx) {
// ...
}
}
这是我对
LambdaMetaFactory
的尝试:try {
Class<?> homeHandlerClass = HomeHandler.class;
Method method = homeHandlerClass.getDeclaredMethod(
"handle", RoutingContext.class);
Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.unreflect(method);
MethodType factoryMethodType = MethodType.methodType(Handler.class);
MethodType functionMethodType = mh.type();
MethodHandle implementationMethodHandle = mh;
Handler<RoutingContext> lambda =
(Handler<RoutingContext>) LambdaMetafactory.metafactory(
lookup,
"handle",
factoryMethodType,
functionMethodType,
implementationMethodHandle,
implementationMethodHandle.type())
.getTarget()
.invokeExact();
lambda.handle(ctx);
} catch (Throwable e) {
e.printStackTrace();
}
这给出了错误:
java.lang.AbstractMethodError: Receiver class [...]$$Lambda$82/0x00000008001fa840
does not define or inherit an implementation of the resolved method abstract
handle(Ljava/lang/Object;)V of interface io.vertx.core.Handler.
我已经尝试了
functionMethodType
和implementationMethodHandle
的一系列其他选项,但尚未设法使它起作用。同样,即使我将RoutingContext.class
引用替换为Object.class
,也无法解决该错误。我可以使
lambda.handle(ctx)
调用成功的唯一方法是更改HomeHandler
,使其不扩展Handler
,将HomeHandler::handle
设置为静态,然后将RoutingContext.class
更改为Object.class
。奇怪的是,即使它不再扩展Handler<RoutingContext>
,我仍然可以将生成的lambda转换为Handler
。我的问题:
LambdaMetaFactory
以使用非静态方法? HomeHandler
,它如何在后台进行实例分配?不管有多少个方法调用,LambdaMetaFactory
是否创建接口(interface)实现的单个实例,因为在此示例中没有捕获的变量?还是为每个方法调用创建一个新实例?还是应该创建一个实例并以某种方式将其传递给API? LambdaMetaFactory
与通用方法一起使用? 编辑:除了以下出色的答案外,我还浏览了此博客文章,解释了其中涉及的机制:
https://medium.freecodecamp.org/a-faster-alternative-to-java-reflection-db6b1e48c33e
最佳答案
是的。 HomeHandler::handle
是一个实例方法,这意味着您需要一个实例来创建功能性接口(interface)包装器,或在每次调用它时传递一个实例(对于它,Handler
不能用作FunctionalInterface类型)。
要使用捕获的实例,您应该:
factoryMethodType
以也接受一个HomeHandler
实例functionMethodType
更改为SAM的已擦除类型,该方法将Object
作为参数。 instantiatedMethodType
参数更改为没有捕获的HomeHandler
实例的目标方法句柄的类型(由于已捕获,因此不再需要它作为参数)。 HomeHandler
的实例传递给invokeExact
。 --
Class<?> homeHandlerClass = HomeHandler.class;
Method method = homeHandlerClass.getDeclaredMethod(
"handle", RoutingContext.class);
Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.unreflect(method);
MethodType factoryMethodType = MethodType.methodType(Handler.class, HomeHandler.class);
MethodType functionMethodType = MethodType.methodType(void.class, Object.class);
MethodHandle implementationMethodHandle = mh;
Handler<RoutingContext> lambda =
(Handler<RoutingContext>) LambdaMetafactory.metafactory(
lookup,
"handle",
factoryMethodType,
functionMethodType,
implementationMethodHandle,
implementationMethodHandle.type().dropParameterTypes(0, 1))
.getTarget()
.invokeExact(new HomeHandler()); // capturing instance
lambda.handle(ctx);
当然,由于
HomeHandler
实现了Handler
,您可以直接使用捕获的实例。new HomeHandler().handle(ctx);
或利用编译器生成元工厂代码,该代码也使用
invokedynamic
,这意味着CallSite
返回的LambdaMetafactory.metafactory
将仅创建一次:Handler<RoutingContext> lambda = new HomeHandler()::handle;
lambda.handle(ctx);
或者,如果功能接口(interface)类型是静态已知的:
MethodHandle theHandle = ...
Object theInstance = ...
MethodHandle adapted = theHandle.bindTo(theInstance);
Handler<RoutingContext> lambda = ctxt -> {
try {
adapted.invokeExact(ctxt);
} catch (Throwable e) {
throw new RuntimeException(e);
}
};
lambda.handle(new RoutingContext());