Можно ли получить ссылку на метод или функциональный объект от объекта путем отражения? - PullRequest
3 голосов
/ 20 апреля 2019

Есть класс А.

class A {
    Mono<Response> testMap(Mono<Request> reqMono)
}

Есть функциональный интерфейс

interface MapHandler {
    Mono<?> handle(Mono<?> reqMono)
}

Теперь я могу написать это

{
A a = new A();
MapHandler handler = a::testMap;
}

И я хочу создать инструмент, который сможет обнаруживать все MapHandler в бине (объекте) и собирать их.

Я пробовал это.

List<MapHandler> list = initedList;
Method method = bean.getClass().getDeclaredMethods()[0];
list.put("methodName", req -> {
    return (Mono<?>) method.invoke(bean, req);
})

Возможно ли это сделать MethodHandle или LambdaMetaFactory?

1 Ответ

1 голос
/ 20 апреля 2019

Грубый набросок решения, которое явно использует LambdaMetafactory так, как вы, кажется, хотите:

// the signature of the method returned by LambdaMetaFactory
// it takes an object of bean's type and makes a MapHandler that calls one
// of its instance methods
MethodType mkLambdaType = MethodType.methodType(MapHandler.class, bean.getClass());
// the signature of the method in MapHandler being implemented
MethodType handleType = MethodType.methodType(Mono.class, Mono.class);
MethodHandles.Lookup lookup = MethodHandles.lookup();

// FYI this won't search in supertypes
// getMethods() will, but you only get public ones even if you have more privileged access
// you decide what to do here; the loop body is the important thing
for(Method method : bean.getClass().getDeclaredMethods()) {
    if(Modifier.isStatic(method.getModifiers())) continue;
    try {
        MethodHandle target = lookup.unreflect(method);
        CallSite mkLambda = LambdaMetafactory.metafactory
            (lookup, "handle", mkLambdaType, handleType, target, handleType);
        list.add((MapHandler)mkLambda.getTarget().invoke(bean));
    } catch(IllegalAccessException | LambdaConversionException e) {
        // because I am lazy, I'm not checking that method has the correct type
        // I'm letting LambdaMetafactory throw if that's not the case
        // if you choose not to use LambdaMetafactory, you may have to implement
        // this type-checking
        continue;
    } catch(Throwable t) {
        // Throwables come from the MethodHandle#invoke call
        // but nothing should be thrown at all, because LambdaMetafactory
        // produces its errors from metafactory, early, which are caught above
        throw new RuntimeException("Unexpected error during reflection", t);
    }
}

Я считаю, что это очень расточительно.Обычная реализация LambdaMetafactory, скорее всего, создаст целый новый класс в вызове metafactory, возвращая CallSite, указывающий на конструктор или подобноеЭто означает, что каждый MapHandler, который вы получаете, является собственным анонимным классом, созданным во время выполнения.Напротив, ваша первоначальная идея использовать лямбду для вызова Method гораздо приятнее JVM.Лямбда вызывает создание одного LambdaMetafactory класса, который содержит method и bean в качестве переменных экземпляра.После первого прогона кода, где invokedynamic загружается, каждый MapHandler создается просто путем создания экземпляра этого анонимного класса.Мое LambdaMetafactory решение кажется приемлемым, только если вам нужно относительно немного MapHandler с, но каждое вызывается так часто, что накладные расходы Method#invoke слишком высоки.

Так что я пошел вперед и сделал несколько быстрых тестов.В вашем случае, когда вы инициализируете MapHandler s при запуске программы, а затем просто вызываете их, моя и ваша техника примерно одинаковы.Они оба настолько быстры, что я не могу измерить разницу во времени, чтобы вызвать различные виды MapHandler.В целом, LambdaMetafactory хуже, потому что создание анонимного класса занимает много времени.На самом деле, чем больше занятий вы проводите, тем дольше это занимает.Между тем, method.invoke лямбда просто должна построить один объект, который часто в тысячи раз быстрее.

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