2 回答
TA贡献1828条经验 获得超6个赞
或者我应该创建一个实例并以某种方式将它传递给 API?
是的。HomeHandler::handle
是一个实例方法,这意味着您需要一个实例来创建功能接口包装器,或者每次调用它时都传递一个实例(对于它Handler
不能作为 FunctionalInterface 类型工作)。
要使用捕获的实例,您应该:
更改
factoryMethodType
为也采取HomeHandler
实例更改
functionMethodType
为 SAM 的擦除类型,该类型以 anObject
作为参数。将
instantiatedMethodType
参数更改为没有捕获HomeHandler
实例的目标方法句柄的类型(因为它已被捕获,因此您不再需要它作为参数)。创建功能接口接口时传递
HomeHandler
to的实例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);
当然,因为HomeHandlerimplements Handler,你可以直接使用捕获的实例;
new HomeHandler().handle(ctx);
或者利用编译器生成元工厂代码,它也使用invokedynamic,这意味着CallSite返回的 byLambdaMetafactory.metafactory只会被创建一次:
Handler<RoutingContext> lambda = new HomeHandler()::handle;
lambda.handle(ctx);
或者,如果功能接口类型是静态知道的:
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());
TA贡献1859条经验 获得超6个赞
既然你说“很遗憾 LambdaMetaFactory API 如此复杂”,那么应该提到它可以做得更简单。
首先,在使用时LambdaMetaFactory,直接使用它:
Lookup lookup = MethodHandles.lookup();
MethodType fType = MethodType.methodType(void.class, RoutingContext.class);
MethodHandle mh = lookup.findVirtual(HomeHandler.class, "handle", fType);
Handler<RoutingContext> lambda = (Handler<RoutingContext>) LambdaMetafactory.metafactory(
lookup, "handle", MethodType.methodType(Handler.class, HomeHandler.class),
fType.erase(), mh, fType).getTarget().invokeExact(new HomeHandler());
您将使用绑定接收器调用实例方法,并且不包括接收器的目标方法的类型与instantiatedMethodType参数相同。此外,由于Tin的边界Handler<T>是Object,您可以简单地使用erase()该方法类型来获取samMethodType参数的擦除签名。
它并不总是那么简单。考虑将方法绑定static int method(int x)到Consumer<Integer>. 那么,samMethodType参数是(Object)void,instantiatedMethodType参数是(Integer)void,而目标方法的签名是int(int)。您需要所有这些参数来正确描述要生成的代码。考虑到其他(前三个)参数通常由 JVM 填充,这种方法已经只需要必要的最小值。
其次,如果您不需要最高性能,则可以简单地使用Proxy基于实现的实现:
MethodHandle mh = MethodHandles.lookup().findVirtual(HomeHandler.class,
"handle", MethodType.methodType(void.class, RoutingContext.class));
Handler<RoutingContext> lambda = MethodHandleProxies.asInterfaceInstance(
Handler.class, mh.bindTo(new HomeHandler()));
添加回答
举报