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

标签 java reflection lambda-metafactory

我正在尝试使用 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;
    }
}

异常(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)

第 182 行是:

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

我知道只用静态方法就可以解决这个问题,但我想知道有没有其他方法可以在不将非静态更改为静态的情况下解决它。

这是 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;
        }
    }

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

最佳答案

问题是你正在使用

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

回复

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

调用bindTo 会创建一个MethodHandle,其第一个参数绑定(bind)到指定的对象实例,但是,您将忽略新的MethodHandle。因此,在调用未绑定(bind)句柄时需要再次指定实例作为参数。

对于此调用,编译时类型很重要。在第一个示例中,参数的编译时类型是正确的,因此调用具有正确的签名 (TestService)Function

在第二个示例中,instance 的编译时类型是Object,因此编译成字节码的签名将是(Object)Function,这不是完全匹配。使用 cls.cast(...) 没有帮助,因为这将执行运行时检查 并断言泛型类型匹配,如果您在此处使用类型变量,但两者都是与 invokeExact 调用的字节码无关。

您有两个选择。您可以简单地使用 invoke 代替,它允许在调用期间进行类型转换(牺牲一点性能)

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

或者您更改代码以执行看似最初预期的操作,在调用之前绑定(bind)第一个参数:

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

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

你也可以把它写成一行

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

关于java - 如何在运行时使用 LambdaMetafactory 访问动态类中的非静态方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53151251/

相关文章:

java - 如何同时创建具有 Autowiring 和非 Autowiring 字段的构造函数?

c# - C#中使用反射反序列化Json

reflection - 通过 MetadataType 属性获取应用于生成实体的自定义属性

java - db4o 如何实例化对象?

java - 使用 LambdaMetafactory 在从其他类加载器获取的类实例上调用单参数方法

java - Mockito:如何匹配任何枚举参数

Java 将字符串拆分为数组

java - 如何执行具有异步任务返回值的方法?

java - 从 LambdaMetafactory 创建 BiConsumer