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

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

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

芜湖不芜 2021-12-30 17:22:54
我正在尝试使用 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,并返回一个实例,它只是一个简单的单例。
查看完整描述

1 回答

?
侃侃无极

TA贡献2051条经验 获得超10个赞

问题是你正在使用


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 omitted

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

或者您更改代码以执行似乎最初打算的操作,请在调用之前绑定第一个参数:


factory = factory.bindTo(instance);

return (Function)factory.invokeExact();

因为对于预先绑定的方法句柄,不需要参数,您再次有一个精确的调用(bindTo不是签名多态的,因此,只会在运行时检查参数的类型)。


你也可以把它写成单行


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


查看完整回答
反对 回复 2021-12-30
  • 1 回答
  • 0 关注
  • 153 浏览

添加回答

举报

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