Приведите java.lang.reflect.Method к функциональному интерфейсу. - PullRequest
0 голосов
/ 04 июля 2019

Сложно найти какие-либо подсказки по теме.Все, что я мог найти, это вопросы о преобразовании одного функционального интерфейса в другой и некоторые статьи о приведении типов в Java.Не то, что я искал.

Этот вопрос о преобразовании lambda → Method, а я хочу наоборот: Method для любого функционального интерфейса, например, Consumer.

Я нашел способ создания лямбда-адаптера для метода Method#invoke:

    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, или это глупость и вышеупомянутое решениес лямбдой нормально как есть?

1 Ответ

4 голосов
/ 04 июля 2019

Если вы довольны использованием отражения под капотом, просто не нравится 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 s вокруг.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...