Java 是否可以通过反射从对象获取方法引用或函数对象?
有一节课Java 是否可以通过反射从对象获取方法引用或函数对象?,java,java-8,Java,Java 8,有一节课 class A { Mono<Response> testMap(Mono<Request> reqMono) } 我想构建一个工具,可以检测bean(对象)中的所有映射处理程序并收集它们 我试过这个 List<MapHandler> list = initedList; Method method = bean.getClass().getDeclaredMethods()[0]; list.put("methodName", req -
class A {
Mono<Response> testMap(Mono<Request> reqMono)
}
我想构建一个工具,可以检测bean(对象)中的所有映射处理程序并收集它们
我试过这个
List<MapHandler> list = initedList;
Method method = bean.getClass().getDeclaredMethods()[0];
list.put("methodName", req -> {
return (Mono<?>) method.invoke(bean, req);
})
List List=initedList;
方法Method=bean.getClass().getDeclaredMethods()[0];
list.put(“methodName”,请求->{
return(Mono)method.invoke(bean,req);
})
是否可以通过
MethodHandle
或LambdaMetaFactory
来实现这一点?以您希望的方式显式使用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调用方法
的最初想法对JVM更有利。lambda导致创建一个LambdaMetafactory
类,该类将方法
和bean
作为实例变量。在第一次运行代码(其中invokedynamic
是引导的)之后,每个MapHandler
都是通过实例化这个匿名类创建的。只有当您只需要相对较少的MapHandler
s时,我的LambdaMetafactory
解决方案才显得可以接受,但每次调用都非常频繁,以至于方法调用的开销太高
所以我已经开始做一些快速的基准测试。在您的用例中,您在程序启动时初始化MapHandler
s,然后只调用它们,我的技术和您的技术大致相同。它们都是如此之快,以至于我无法实际测量调用不同类型的MapHandler
所花费的时间差异。一般来说,LambdaMetafactory
更糟糕,因为创建匿名类需要很多时间。事实上,你上的课越多,花的时间就越长。同时,method.invoke
lambda只需构造一个对象,速度通常要快上千倍。>这意味着您得到的每个MapHandler都是它自己的匿名类。我的解决方案是()->{}
包装method.invoke()
,在编译阶段LambdaMetafactory似乎做了同样的事情,但调用方法#invoke
>方法#invoke
的开销太高。您的解决方案似乎通过创建一个新的映射处理程序来减少方法#invoke
。我可以问一个新问题吗?如果我不关心编译性能和这个工具的性能,只关心创建的函数是否会因为反射而影响性能。我的解决方案和您的解决方案创建的函数之间有什么区别吗?是的,此解决方案减少了Method\invoke
调用的数量。不,那还不够好,不能比你的快。您的创建了一个匿名类。该类在第一次执行代码时在运行时生成,然后重新使用。这将在运行时为每个MapHandler
生成全新的类。它贵得离谱。编译性能与此无关。此外,一旦有了方法
,调用invoke
的速度只有调用该方法的两倍。反射的真正代价是查找方法,这需要10倍的时间。我不确定由LambdaMetafactory创建的函数对象是否比调用方法.invoke()快。我目前正在测试它。如果您在同一方法
对象上调用invoke
的次数足够多,它也会在后台生成一个新的invoker类。中间解决方案是使用unreflect
返回的直接MethodHandle
进行调用。但是由于问题的代码片段表明将维护从方法名到实际调用代码的映射,我会毫不犹豫地使用LambdaMetafactory
生成的类,因为数量是有限的,而且易于管理……您想检测某个bean中符合MapHandler
声明的所有方法吗?您拥有的代码1)不会编译invode
vsinvoke
2)如果该方法符合MapHandler
的要求,则不会“测试”-是否需要更清楚一点?
List<MapHandler> list = initedList;
Method method = bean.getClass().getDeclaredMethods()[0];
list.put("methodName", req -> {
return (Mono<?>) method.invoke(bean, req);
})
// 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);
}
}