将java.lang.reflect.Method强制转换为功能接口

时间:2019-07-04 08:56:07

标签: java reflection

很难找到该主题的任何线索。我只能找到有关将一个功能接口转换为另一个功能接口的问题,以及一些有关Java中类型转换的文章。不是我要找的东西。

This的问题是关于转换lambda → Method的,我想相反,是将Method转换为任何功能接口,例如,转换为Consumer

我发现的方法是围绕Method#invoke方法创建一个lambda适配器:

    public void registerCallbacks(final Object annotated) {
        Class clazz = annotated.getClass();
        for (Method method : clazz.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Callback.class)) {
                Callback registration = method.getAnnotation(Callback.class);

                List<String> warnings = new ArrayList<>(3);

                if (!Modifier.isPublic(method.getModifiers()))
                    warnings.add(String.format("Method %s must be public", method));
                if (method.getParameterCount() != 1)
                    warnings.add(String.format("Method %s must consume only one argument", method));
                if (method.getParameterCount() == 1 && !method.getParameterTypes()[0].equals(Integer.class))
                    warnings.add(String.format("Method %s must consume %s", method, Integer.class));

                if (!warnings.isEmpty()) {
                    warnings.forEach(log::warn);
                    continue;
                }

                CALLBACKS_MAPPER.registerCallback((param) -> {
                    try {
                        method.invoke(annotated, param);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        // Should not happen due to checks before.
                        log.warn(String.format("Could not invoke %s on %s with %s", method, annotated, param), e);
                    }
                });
                log.info("Registered {} as a callback", method);
            }
        }
    }

但是我要避免写

    CALLBACKS_MAPPER.registerCallback((param) -> {
        try {
            method.invoke(annotated, param);
        } catch (IllegalAccessException | InvocationTargetException e) {
            // Should not happen due to checks before.
            log.warn(String.format("Could not invoke %s on %s with %s", method, annotated, param), e);
        }
    });

支持更简单的内容,例如

    CALLBACKS_MAPPER.registerCallback(SomeApacheLib.methodToFunction(annotated, method));

➥那么,有没有办法将旧的Java 1.1反射库映射到较新的Java 8功能接口,还是我很笨,上面提到的用lambda的解决方案就可以了?

1 个答案:

答案 0 :(得分:4)

如果您对在幕后使用反射感到满意,只是不喜欢try周围的catch / invoke,则可以制作一个简单的实用函数,例如:

public static <T> Consumer<T> toConsumer(Object annotated, Method m) {
    return param -> {
        try {
            m.invoke(annotated, param);
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    };
}

这为您提供了所需的语法:

CALLBACKS_MAPPER.registerCallback(toConsumer(annotated, method));

但是,如果您想完全避免反射,可以使用LambdaMetafactory创建一个Consumer

static Consumer<String> toConsumer(MethodHandles.Lookup lookup, Object annotated, Method method) throws Throwable {
    MethodType consumeString = MethodType.methodType(void.class, String.class);
    MethodHandle handle = lookup.unreflect(method);
    final CallSite site = LambdaMetafactory.metafactory(lookup, "accept",
            MethodType.methodType(Consumer.class, annotated.getClass()),
            consumeString.changeParameterType(0, Object.class),
            handle,
            consumeString);
    return (Consumer<String>) site.getTarget().invoke(annotated);
}

String更改为您的回调应接受的值。然后:

CALLBACKS_MAPPER.registerCallback(toConsumer(MethodHandles.lookup(), annotated, method));

当然,这里唯一合适的解决方案是重构代码以使用已知的回调接口,在该接口上通常可以调用定义的方法,而不用传递Method来代替。