猿问

如何在运行时使用 LambdaMetafactory 访问动态类中的非静态方法

我正在尝试使用 LambdaMetafactory 来替换反射,但是我遇到了问题。如果我使用特定的类,那么它运行良好,就像这样:


        MethodHandles.Lookup lookup = MethodHandles.lookup();

        MethodType type = MethodType.methodType(ResponseMsg.class,Map.class);


        MethodHandle mh = lookup.findVirtual(TestService.class,"testMethod",type);


        TestService ins = TestService.getInstance();


        MethodHandle factory = LambdaMetafactory.metafactory(

                lookup, "apply", MethodType.methodType(Function.class,TestService.class),

                type.generic(), mh, type).getTarget();


        factory.bindTo(ins);


        Function lambda = (Function) factory.invokeExact(ins);

但是如果我Class<?>用来替换特定的类,那么它就行不通了,就像这样:


    public static Function generateLambda(@NotNull Class<?> cls,@NotNull String method) {

    MethodHandles.Lookup lookup = MethodHandles.lookup();

    MethodType type = MethodType.methodType(RETURN_TYPE,PARM_TYPE);


    try {

        MethodHandle mh = lookup.findVirtual(cls,method,type);

        Object instance = getInstance(cls);

        if(instance == null) {

            return null;

        }

        MethodHandle factory = LambdaMetafactory.metafactory(

                lookup, "apply", MethodType.methodType(Function.class,cls),

                type.generic(), mh, type).getTarget();


        factory.bindTo(cls.cast(instance));


        return (Function) factory.invokeExact(cls.cast(instance));

    } catch (Throwable e) {

        logger.error("get Function fail, cause :" ,e);

        return null;

    }

}

这是例外:


java.lang.invoke.WrongMethodTypeException: expected (TestService)Function but found (Object)Function

    at java.lang.invoke.Invokers.newWrongMethodTypeException(Invokers.java:298)

    at java.lang.invoke.Invokers.checkExactType(Invokers.java:309)

    at com.utils.cache.ClassUtils.generateLambda(ClassUtils.java:182)

第 182 行是:


return (Function) factory.invokeExact(cls.cast(instance));

我知道只使用静态方法可以解决这个问题,但我想知道有没有其他方法可以解决它而无需将非静态更改为静态。


在这个方法中,我使用反射找到Class中的静态方法getInstance,并返回一个实例,它只是一个简单的单例。


芜湖不芜
浏览 154回答 1
1回答

侃侃无极

问题是你正在使用factory.bindTo(ins);Function lambda = (Function) factory.invokeExact(ins);分别factory.bindTo(cls.cast(instance));return (Function) factory.invokeExact(cls.cast(instance));调用bindTo会创建一个MethodHandle,其第一个参数绑定到指定的对象实例,但是,您忽略了新的MethodHandle. 因此,在调用未绑定句柄时,您需要再次将实例指定为参数。对于此调用,编译时类型很重要。在第一个示例中,参数的编译时类型是正确的,因此调用具有正确的签名(TestService)Function。在第二个示例中, 的编译时类型instance是Object,因此编译成字节码的签名将是(Object)Function,这不是完全匹配。使用cls.cast(…)无济于事,因为这将执行运行时检查并断言泛型类型匹配,如果您在这里使用类型变量,但两者都与invokeExact调用的字节码无关。你有两个选择。您可以简单地使用invoke,它允许在调用期间进行类型转换(牺牲一点性能)// unused factory.bindTo call omittedreturn (Function) factory.invoke(instance); // noneffective cls.cast omitted或者您更改代码以执行似乎最初打算的操作,请在调用之前绑定第一个参数:factory = factory.bindTo(instance);return (Function)factory.invokeExact();因为对于预先绑定的方法句柄,不需要参数,您再次有一个精确的调用(bindTo不是签名多态的,因此,只会在运行时检查参数的类型)。你也可以把它写成单行return (Function)factory.bindTo(instance).invokeExact();
随时随地看视频慕课网APP

相关分类

Java
我要回答