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

时间:2019-04-20 14:27:57

标签: java java-8

有一堂课。

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来做到这一点?

1 个答案:

答案 0 :(得分: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。仅当您只需要相对较少的LambdaMetafactory时,我的MapHandler解决方案才可以接受,但是每次调用都如此频繁,以至于Method#invoke的开销太高了。


因此,我继续进行了一些快速基准测试。在您的用例中,您在程序启动时初始化MapHandler,然后仅调用它们,我的技术和您的技术大致相同。它们都太快了,以至于我实际上无法测量调用各种MapHandler所花费的时间差异。通常,LambdaMetafactory更为糟糕,因为创建匿名类会花费很多时间。实际上,您制作的课程越多,花费的时间就越长。同时,method.invoke lambda只需构造一个对象,通常速度快数千倍。