将 java.lang.reflect.Method 转换为函数式接口 [英] Cast java.lang.reflect.Method to a functional interface
问题描述
很难找到有关该主题的任何线索.我能找到的只是关于将一个函数式接口转换为另一个函数式接口的问题,以及一些关于 Java 中类型转换的文章.不是我要找的.
It is difficult to find any clues for the topic. All I could find is questions about converting one functional interface to another and some articles on type casting in Java. Not what I was looking for.
这个问题是关于转换 lambda → Method
,我想要相反的,转换 Method
到任何功能接口,例如,到 Consumer
.
This question is about converting lambda → Method
and I want the opposite, to convert Method
to any functional interface, for example, to Consumer
.
我发现的方法是围绕 Method#invoke
方法创建一个 lambda 适配器:
The way I found is to create a lambda adapter around the Method#invoke
method:
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);
}
});
赞成更简单的东西,比如
in favor of something simpler, like
CALLBACKS_MAPPER.registerCallback(SomeApacheLib.methodToFunction(annotated, method));
➥ 那么,有没有办法将旧的 Java 1.1 反射库映射到新的 Java 8 功能接口,或者是我太笨了,上面提到的 lambda 解决方案很好?
➥ So, is there a way to map old Java 1.1 reflection library to newer Java 8 functional interfaces, or it is me being stupid and the abovementioned solution with lambda is fine as it is?
推荐答案
如果你满足于在幕后使用反射,只是不喜欢 try
/catch
围绕 invoke
,你可以做一个简单的实用函数,如:
If you're content with using reflection under the hood, just don't like the try
/catch
around the invoke
, you can just make a simple utility function like:
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
:
But if you want to actually avoid reflection altogether, you can use LambdaMetafactory
to create a 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
更改为您的回调预期接受的任何内容.然后:
Change String
to whatever your callbacks are expected to accept. And then:
CALLBACKS_MAPPER.registerCallback(toConsumer(MethodHandles.lookup(), annotated, method));
当然,这里唯一合适的解决方案是重构代码以使用已知的回调接口,您通常可以在该回调接口上调用定义的方法,而不是传递 Method
.
Of course, the only proper solution here is that you refactor your code to use a known callback interface on which you can normally call a defined method, instead of passing Method
s around.
这篇关于将 java.lang.reflect.Method 转换为函数式接口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!