B3N*_*3NW 5 java reflection lambda java-8
尽管已经阅读了我所知道的所有文档,但我无法解决使用lambdas执行方法的问题.为了给出一些背景知识,我的用例是一个插件系统.我正在使用一个可以分配给任何方法的注释(@EventHandle).我使用反射并遍历类中的每个方法并检查它是否具有注释,如果是,则将方法添加到处理程序对象(将其添加到列表以处理每个"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);
}
}
Run Code Online (Sandbox Code Playgroud)
在这个类的当前迭代中,我直接转向功能接口EventHandler,source:
package me.b3nw.dev.Events;
@FunctionalInterface
public interface EventHandler {
boolean handle(GameEvent evt);
}
Run Code Online (Sandbox Code Playgroud)
目前我收到以下错误:
ERROR m.b.d.H.GamemodeHandler -
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]
Run Code Online (Sandbox Code Playgroud)
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]
Run Code Online (Sandbox Code Playgroud)
修改后的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();
}
}
Run Code Online (Sandbox Code Playgroud)
这个类有一个注释的方法,应该实现功能接口的签名但是......显然不是:(这是类:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
我如何解决这个问题,我在这里使用lambdas是完全错误的吗?
谢谢.
如果您查看日志输出,您会注意到您的目标方法签名看起来像(Lme/b3nw/dev/Events/Vanilla;Lme/b3nw/dev/Events/GameEvent;)Z,换句话说,由于您的目标方法是实例方法,因此它需要其类的实例(即Vanilla)作为第一个参数。
如果您在 lambda 创建时不提供实例,但将目标方法 xe2x80x99s 签名作为函数签名传递,则创建的 lambda 实例将具有如下方法
\n\nboolean handle(Vanilla instance, GameEvent evt) { instance.testMethod2(evt); }\nRun Code Online (Sandbox Code Playgroud)\n\n这与\xe2\x80\x99不匹配真正的interface方法
boolean handle(GameEvent evt);\nRun Code Online (Sandbox Code Playgroud)\n\n您正在尝试调用它。因此你会得到一个AbstractMethodError. 首先LambdaMetaFactory用于编译器生成的代码,并且不会执行昂贵的检查,即不会尝试确定功能接口方法以将其与提供的签名进行比较。
因此,您要做的就是提供应该调用目标方法的实例:
\n\npublic Handler(Method method, Object instance, EventHandle handle) throws Throwable {\n this.method = method;\n\n log.info(method.getGenericReturnType() + "");\n\n for(Type type : method.getParameterTypes()) {\n log.info(type.getTypeName());\n }\n\n this.handle = handle;\n this.lookup = MethodHandles.lookup();\n this.methodHandle = lookup.unreflect(method);\n MethodType type = methodHandle.type();\n // add the type of the instance to the factory method\n MethodType factoryType=MethodType.methodType(EventHandler.class,type.parameterType(0));\n // and remove it from the function signature\n type=type.dropParameterTypes(0, 1);\n\n log.info("" + type.toMethodDescriptorString());\n\n this.invoker = (EventHandler)LambdaMetafactory.metafactory(lookup,\n "handle", factoryType, type, methodHandle, type).getTarget()\n // use invoke instead of invokeExact as instance is declared as Object\n .invoke(instance);\n}\nRun Code Online (Sandbox Code Playgroud)\n\n当然,您还必须调整收集带注释的方法的代码以传递您正在处理的实例。
\n\n请注意,这都是指您的第一个版本。我无法\xe2\x80\x99t 理解你的\xe2\x80\x9cmodified Handler\xe2\x80\x9d 的要点。
\n| 归档时间: |
|
| 查看次数: |
1017 次 |
| 最近记录: |