是否可以通过反射从对象获取方法引用或函数对象?

问题描述 投票:3回答:1

有一节课。

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

有功能界面

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

现在我可以写这个

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

我想构建一个可以检测bean(对象)中的所有MapHandler并收集它们的工具。

我试过这个。

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

可以通过MethodHandleLambdaMetaFactory来实现吗?

java java-8
1个回答
1
投票

以您希望的方式明确使用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都是它自己的匿名类,在运行时创建。相比之下,使用lambda调用Method的最初想法对于JVM来说要好得多。 lambda导致创建单个LambdaMetafactory类,它将methodbean保存为实例变量。在第一次运行代码之后,invokedynamic被引导,每个MapHandler只是通过实例化这个匿名类来创建。如果你只需要相对较少的LambdaMetafactorys,我的MapHandler解决方案似乎是可以接受的,但是每个都经常被调用,以至于Method#invoke的开销太高了。


所以我已经完成了一些快速的基准测试。在你的用例中,你在程序启动时初始化MapHandlers,然后只是调用它们,我的技术和你的技术大致相同。它们都是如此之快,我实际上无法衡量调用不同种类的MapHandler所花费的时间差。一般来说,LambdaMetafactory更糟糕,因为创建匿名类需要花费很多时间。事实上,你制作的课程越多,花费的时间就越长。同时,method.invoke lambda只需构造一个对象,通常要快几千倍。

© www.soinside.com 2019 - 2024. All rights reserved.