简体   繁体   English

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

[英]How to access a non-static method in dynamic class with LambdaMetafactory during the runtime

I am trying to use LambdaMetafactory to replace reflection,but I have a problem.If I use a specific class then it works well,just like this:我正在尝试使用 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);

But if I use Class<?> to replace the specific class ,then it won't work,just like this:但是如果我使用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;
    }
}

Here is the exception:这是例外:

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)

The Line 182 is:第 182 行是:

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

I know just use static method can solve this problem,but I want to know is there any other way to solve it without changing the non-static to static.我知道只使用静态方法可以解决这个问题,但我想知道有没有其他方法可以解决它而无需将非静态更改为静态。

Here is the getInstance:这是getInstance:

 private static Object getInstance(@NotNull Class<?> cls) {
        try {
            Method getInstanceMethod = cls.getDeclaredMethod("getInstance");
            return getInstanceMethod.invoke(null);
        } catch (Exception e) {
            logger.error("get instance fail, cause :" ,e);
            return null;
        }
    }

In this method,I use reflection to find the static method getInstance in Class,and return a instance,it is just a simple singleton.在这个方法中,我使用反射找到Class中的静态方法getInstance,并返回一个实例,它只是一个简单的单例。

The problem is that you are using问题是你正在使用

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

resp.分别

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

Calling bindTo creates a MethodHandle , whose first parameter is bound to the specified object instance, however, you are ignoring the new MethodHandle .调用bindTo会创建一个MethodHandle ,其第一个参数绑定到指定的对象实例,但是,您忽略了新的MethodHandle Therefore, you need to specify the instance again as an argument when invoking the unbound handle.因此,在调用未绑定句柄时,您需要再次将实例指定为参数。

For this invocation, the compile-time type matters.对于此调用,编译时类型很重要。 In the first example, the compile-time type of the argument is correct, hence the invocation has the right signature (TestService)Function .在第一个示例中,参数的编译时类型是正确的,因此调用具有正确的签名(TestService)Function

In the second example, the compile-time type of instance is Object , hence the signature compiled into the byte code will be (Object)Function , which is not an exact match.在第二个例子中, instance的编译时类型是Object ,因此编译成字节码的签名将是(Object)Function ,这不是完全匹配。 Using cls.cast(…) does not help, as that will perform a runtime check and assert that generic types match, if you used a type variable here, but both is irrelevant to the byte code of the invokeExact call.使用cls.cast(…)没有帮助,因为如果在这里使用类型变量,它将执行运行时检查并断言泛型类型匹配,但两者都与invokeExact调用的字节码无关。

You have two options.你有两个选择。 You can simply use invoke instead, which allows type conversions during the invocation (sacrificing a bit performance)您可以简单地使用invoke代替,它允许在调用期间进行类型转换(牺牲一点性能)

// unused factory.bindTo call omitted
return (Function) factory.invoke(instance); // noneffective cls.cast omitted

or you change the code to do what seems to be originally intended, bind the first argument before the invocation:或者您更改代码以执行似乎最初打算的操作,请在调用之前绑定第一个参数:

factory = factory.bindTo(instance);
return (Function)factory.invokeExact();

since for the pre-bound method handle, no argument is needed, you have an exact call again ( bindTo is not signature polymorphic, hence, will check the type of the argument only at runtime).因为对于预先绑定的方法句柄,不需要参数,您再次有一个精确的调用( bindTo不是签名多态的,因此,只会在运行时检查参数的类型)。

You could also write this as a one-liner你也可以把它写成单行

return (Function)factory.bindTo(instance).invokeExact();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM