我应该如何在我的用例中使用LambdaMetaFactory?

尽管已经阅读了我所知道的所有文档,但我无法解决使用lambdas执行方法的问题。 为了给出一些背景知识,我的用例是一个插件系统。 我正在使用可以分配给任何方法的注释(@EventHandle)。 我使用reflection并遍历类中的每个方法并检查它是否具有注释,如果是,则将方法添加到处理程序对象(将其添加到列表以处理每个“tick”)。 这是我的处理程序类:

package me.b3nw.dev.Events; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import java.lang.invoke.*; import java.lang.reflect.Method; import java.lang.reflect.Type; @Slf4j public class Handler { @Getter private final Method method; @Getter private final EventHandle handle; private final MethodHandles.Lookup lookup; private final MethodHandle methodHandle; private final EventHandler invoker; public Handler(Method method, EventHandle handle) throws Throwable { this.method = method; log.info(method.getGenericReturnType() + ""); for(Type type : method.getParameterTypes()) { log.info(type.getTypeName()); } this.handle = handle; this.lookup = MethodHandles.lookup(); this.methodHandle = lookup.unreflect(method); log.info("" + methodHandle.type().toMethodDescriptorString()); this.invoker = (EventHandler) LambdaMetafactory.metafactory(lookup, "handle", MethodType.methodType(EventHandler.class), methodHandle.type(), methodHandle, methodHandle.type()).getTarget().invokeExact(); } public void invoke(GameEvent evt) throws Throwable { invoker.handle(evt); } } 

在这个类的当前迭代中,我直接转向function接口EventHandler,source:

 package me.b3nw.dev.Events; @FunctionalInterface public interface EventHandler { boolean handle(GameEvent evt); } 

目前我收到以下错误:

 ERROR mbdHGamemodeHandler - java.lang.AbstractMethodError: me.b3nw.dev.Events.Handler$$Lambda$3/1704984363.handle(Lme/b3nw/dev/Events/GameEvent;)Z at me.b3nw.dev.Events.Handler.invoke(Handler.java:40) ~[classes/:na] at me.b3nw.dev.Handlers.GamemodeHandler.userEventTriggered(GamemodeHandler.java:34) ~[classes/:na] 

GamemodeHandler只调用Handler类中的invoke方法。

因此,当我直接转换为EventHandler并执行时,它输出一个AbstractMethodError,当我不转换它时,我得到一个不同的错误,即:

 java.lang.invoke.WrongMethodTypeException: expected ()EventHandler but found ()void at java.lang.invoke.Invokers.newWrongMethodTypeException(Invokers.java:294) ~[na:1.8.0_45] at java.lang.invoke.Invokers.checkExactType(Invokers.java:305) ~[na:1.8.0_45] at me.b3nw.dev.Events.Handler.invoke(Handler.java:40) ~[classes/:na] at me.b3nw.dev.Handlers.GamemodeHandler.userEventTriggered(GamemodeHandler.java:34) ~[classes/:na] 

修改后的Handler反映变化:

 package me.b3nw.dev.Events; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import java.lang.invoke.*; import java.lang.reflect.Method; import java.lang.reflect.Type; @Slf4j public class Handler { @Getter private final Method method; @Getter private final EventHandle handle; private final MethodHandles.Lookup lookup; private final MethodHandle methodHandle; private final MethodHandle invoker; public Handler(Method method, EventHandle handle) throws Throwable { this.method = method; log.info(method.getGenericReturnType() + ""); for(Type type : method.getParameterTypes()) { log.info(type.getTypeName()); } this.handle = handle; this.lookup = MethodHandles.lookup(); this.methodHandle = lookup.unreflect(method); log.info("" + methodHandle.type().toMethodDescriptorString()); this.invoker = LambdaMetafactory.metafactory(lookup, "handle", MethodType.methodType(EventHandler.class), methodHandle.type(), methodHandle, methodHandle.type()).getTarget(); } public void invoke(GameEvent evt) throws Throwable { invoker.invokeExact(); } } 

这个类有一个注释的方法,应该实现function接口的签名但是……显然不是:(这是类:

 package me.b3nw.dev.Gamemode; import lombok.extern.slf4j.Slf4j; import me.b3nw.dev.Events.EventHandle; import me.b3nw.dev.Events.GameEvent; @Slf4j public class Vanilla extends Gamemode { public void testMethod() { } @EventHandle(type = EventHandle.Type.NICKANNOUNCE) public boolean testMethod2(GameEvent evt) { log.info("Fuck yeah!"/* + evt*/); return true; } } 

我如何解决这个问题,我在这里使用lambdas是完全错误的吗?

谢谢。

如果您查看了日志输出,您会注意到您的目标方法签名看起来像(Lme/b3nw/dev/Events/Vanilla;Lme/b3nw/dev/Events/GameEvent;)Z ,换句话说,因为您的目标方法是实例方法,它需要一个类的实例(即Vanilla )作为第一个参数。

如果您没有在lambda创建时提供实例但是将目标方法的签名作为function签名传递,则创建的lambda实例将具有类似的方法

 boolean handle(Vanilla instance, GameEvent evt) { instance.testMethod2(evt); } 

这与真实的interface方法不符

 boolean handle(GameEvent evt); 

你试图调用它。 因此,您得到一个AbstractMethodErrorLambdaMetaFactory首先用于编译器生成的代码,并且不执行昂贵的检查,即不尝试确定将其与提供的签名进行比较的function接口方法。

所以你要做的是提供应该调用目标方法的实例:

 public Handler(Method method, Object instance, EventHandle handle) throws Throwable { this.method = method; log.info(method.getGenericReturnType() + ""); for(Type type : method.getParameterTypes()) { log.info(type.getTypeName()); } this.handle = handle; this.lookup = MethodHandles.lookup(); this.methodHandle = lookup.unreflect(method); MethodType type = methodHandle.type(); // add the type of the instance to the factory method MethodType factoryType=MethodType.methodType(EventHandler.class,type.parameterType(0)); // and remove it from the function signature type=type.dropParameterTypes(0, 1); log.info("" + type.toMethodDescriptorString()); this.invoker = (EventHandler)LambdaMetafactory.metafactory(lookup, "handle", factoryType, type, methodHandle, type).getTarget() // use invoke instead of invokeExact as instance is declared as Object .invoke(instance); } 

当然,您还必须调整收集带注释方法的代码,以便通过您正在处理的实例。

请注意,这一切都是指您的第一个版本。 我无法理解你的“修改过的Handler”。