猿问

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

我试图使用Java的LambdaMetaFactory动态实现一个通用的拉姆达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;


            .getTarget()

            .invokeExact();


    lambda.handle(ctx);


这给出了错误:


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.

我已经为functionMethodTypeand尝试了一系列其他选项implementationMethodHandle,但还没有设法让它工作。此外,即使我将RoutingContext.class引用替换为Object.class,也无法修复错误。


我可以使lambda.handle(ctx)调用成功的唯一方法是更改HomeHandler以使其不扩展Handler、HomeHandler::handle静态化并更改RoutingContext.class为Object.class. 奇怪的是,我仍然可以将生成的 lambda 转换为Handler<RoutingContext>,即使它不再扩展Handler。

我的问题:

  1. 我如何开始LambdaMetaFactory使用非静态方法?

  2. 对于这个非静态 SAM 类HomeHandler,它如何在后台处理实例分配?是否LambdaMetaFactory创建接口实现的单个实例,无论有多少方法调用,因为在此示例中没有捕获的变量?还是为每个方法调用创建一个新实例?或者我应该创建一个实例并以某种方式将它传递给 API?

  3. 我如何开始LambdaMetaFactory使用泛型方法?

编辑:除了下面的好答案外,我还看到了这篇博文,解释了所涉及的机制:

https://medium.freecodecamp.org/a-faster-alternative-to-java-reflection-db6b1e48c33e


慕慕森
浏览 255回答 2
2回答

30秒到达战场

或者我应该创建一个实例并以某种方式将它传递给 API?是的。HomeHandler::handle是一个实例方法,这意味着您需要一个实例来创建功能接口包装器,或者每次调用它时都传递一个实例(对于它Handler不能作为 FunctionalInterface 类型工作)。要使用捕获的实例,您应该:更改factoryMethodType为也采取HomeHandler实例更改functionMethodType为 SAM 的擦除类型,该类型以 anObject作为参数。将instantiatedMethodType参数更改为没有捕获HomeHandler实例的目标方法句柄的类型(因为它已被捕获,因此您不再需要它作为参数)。创建功能接口接口时传递HomeHandlerto的实例invokeExact。——Class<?> homeHandlerClass = HomeHandler.class;Method method = homeHandlerClass.getDeclaredMethod(&nbsp; &nbsp; &nbsp; &nbsp; "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 =&nbsp; &nbsp; &nbsp; &nbsp; (Handler<RoutingContext>) LambdaMetafactory.metafactory(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lookup,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "handle",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; factoryMethodType,&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; functionMethodType,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; implementationMethodHandle,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; implementationMethodHandle.type().dropParameterTypes(0, 1))&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; .getTarget()&nbsp; &nbsp; &nbsp; &nbsp; .invokeExact(new HomeHandler()); // capturing instancelambda.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 -> {&nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; adapted.invokeExact(ctxt);&nbsp; &nbsp; } catch (Throwable e) {&nbsp; &nbsp; &nbsp; &nbsp; throw new RuntimeException(e);&nbsp; &nbsp; }};lambda.handle(new RoutingContext());

BIG阳

既然你说“很遗憾 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(&nbsp; &nbsp; lookup, "handle", MethodType.methodType(Handler.class, HomeHandler.class),&nbsp; &nbsp; 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,&nbsp; &nbsp; "handle", MethodType.methodType(void.class, RoutingContext.class));Handler<RoutingContext> lambda = MethodHandleProxies.asInterfaceInstance(&nbsp; &nbsp; Handler.class, mh.bindTo(new HomeHandler()));
随时随地看视频慕课网APP

相关分类

Java
我要回答