[英]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.