为了账号安全,请及时绑定邮箱和手机立即绑定

LambdaMetaFactory 具有泛型类型的具体实现

LambdaMetaFactory 具有泛型类型的具体实现

慕慕森 2021-11-17 15:04:35
我试图使用Java的LambdaMetaFactory动态实现一个通用的拉姆达Handler<RoutingContext>:public class RoutingContext {    // ...}@FunctionalInterfacepublic 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;            .getTarget()            .invokeExact();    lambda.handle(ctx);} 这给出了错误:java.lang.AbstractMethodError: Receiver class [...]$$Lambda$82/0x00000008001fa840does not define or inherit an implementation of the resolved method abstracthandle(Ljava/lang/Object;)V of interface io.vertx.core.Handler.我已经为functionMethodTypeand尝试了一系列其他选项implementationMethodHandle,但还没有设法让它工作。此外,即使我将RoutingContext.class引用替换为Object.class,也无法修复错误。我可以使lambda.handle(ctx)调用成功的唯一方法是更改HomeHandler以使其不扩展Handler、HomeHandler::handle静态化并更改RoutingContext.class为Object.class. 奇怪的是,我仍然可以将生成的 lambda 转换为Handler<RoutingContext>,即使它不再扩展Handler。我的问题:我如何开始LambdaMetaFactory使用非静态方法?对于这个非静态 SAM 类HomeHandler,它如何在后台处理实例分配?是否LambdaMetaFactory创建接口实现的单个实例,无论有多少方法调用,因为在此示例中没有捕获的变量?还是为每个方法调用创建一个新实例?或者我应该创建一个实例并以某种方式将它传递给 API?我如何开始LambdaMetaFactory使用泛型方法?编辑:除了下面的好答案外,我还看到了这篇博文,解释了所涉及的机制:https://medium.freecodecamp.org/a-faster-alternative-to-java-reflection-db6b1e48c33e
查看完整描述

2 回答

?
30秒到达战场

TA贡献1828条经验 获得超6个赞

或者我应该创建一个实例并以某种方式将它传递给 API?

是的。HomeHandler::handle是一个实例方法,这意味着您需要一个实例来创建功能接口包装器,或者每次调用它时都传递一个实例(对于它Handler不能作为 FunctionalInterface 类型工作)。

要使用捕获的实例,您应该:

  • 更改factoryMethodType为也采取HomeHandler实例

  • 更改functionMethodType为 SAM 的擦除类型,该类型以 anObject作为参数。

  • instantiatedMethodType参数更改为没有捕获HomeHandler实例的目标方法句柄的类型(因为它已被捕获,因此您不再需要它作为参数)。

  • 创建功能接口接口时传递HomeHandlerto的实例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());


查看完整回答
反对 回复 2021-11-17
?
BIG阳

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()));


查看完整回答
反对 回复 2021-11-17
  • 2 回答
  • 0 关注
  • 254 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信