很难找到该主题的任何线索。我只能找到有关将一个功能接口转换为另一个功能接口的问题,以及一些有关Java中类型转换的文章。不是我要找的东西。
这个问题是关于转换的,lambda →Method而我想相反地转换Method为任何功能接口,例如to Consumer。
lambda →Method
Method
Consumer
我发现的方法是围绕该Method#invoke方法创建一个lambda适配器:
Method#invoke
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); } });
赞成更简单的事情,例如
CALLBACKS_MAPPER.registerCallback(SomeApacheLib.methodToFunction(annotated, method));
➥因此,有没有办法将旧的Java 1.1反射库映射到较新的Java 8功能接口,还是我很笨,上面提到的用lambda的解决方案就可以了?
如果您对在幕后使用反射感到满意,只是不喜欢try/ catch周围的/ invoke,则可以制作一个简单的实用函数,例如:
try
catch
invoke
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:
LambdaMetafactory
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为您的回调应接受的内容。然后:
String
CALLBACKS_MAPPER.registerCallback(toConsumer(MethodHandles.lookup(), annotated, method));
当然,这里唯一合适的解决方案是重构代码以使用已知的回调接口,在该接口上通常可以调用定义的方法,而不用传递Methods。