Java 我应该如何在我的用例中使用LambdaMetaFactory?
尽管阅读了我所知道的所有文档,但我无法解决使用lambdas执行方法的问题。为了给大家一点背景知识,我的用例是一个插件系统。我使用的注释(@EventHandle)可以分配给任何方法。我使用反射并迭代类中的每个方法,检查它是否有注释,如果有,则将该方法添加到处理程序对象(该对象添加到列表中以处理每个“勾号”)。这是我的处理程序类:Java 我应该如何在我的用例中使用LambdaMetaFactory?,java,reflection,lambda,java-8,Java,Reflection,Lambda,Java 8,尽管阅读了我所知道的所有文档,但我无法解决使用lambdas执行方法的问题。为了给大家一点背景知识,我的用例是一个插件系统。我使用的注释(@EventHandle)可以分配给任何方法。我使用反射并迭代类中的每个方法,检查它是否有注释,如果有,则将该方法添加到处理程序对象(该对象添加到列表中以处理每个“勾号”)。这是我的处理程序类: package me.b3nw.dev.Events; import lombok.Getter; import lombok.extern.slf4j.Slf4j
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);
}
}
在这个类的当前迭代中,我将直接转换到函数接口EventHandler,source:
package me.b3nw.dev.Events;
@FunctionalInterface
public interface EventHandler {
boolean handle(GameEvent evt);
}
目前我得到以下错误:
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]
GamemodeHandler只调用处理程序类中的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]
修改处理程序以反映更改:
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();
}
}
这个类有一个被注释的方法,应该实现函数接口的签名,但是。。显然不是:(这是课堂:
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创建时不提供实例,而是将目标方法的签名作为函数签名传递,则创建的lambda实例将具有如下方法
boolean handle(Vanilla instance, GameEvent evt) { instance.testMethod2(evt); }
与实际的接口
方法不匹配
boolean handle(GameEvent evt);
您正试图调用它。因此,您会得到一个AbstractMethodError
。LambdaMetaFactory
首先用于编译器生成的代码,并且不执行昂贵的检查,即不尝试确定函数接口方法以将其与提供的签名进行比较
因此,您需要做的是提供应该在其上调用目标方法的实例:
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);
}
当然,您还必须调整收集带注释方法的代码,以通过正在处理的实例
请注意,这些都是指您的第一个版本。我无法理解您的“修改处理程序”的要点.为什么要使用LambdaMetafactory?似乎您可以使用调用invokeExact的实际Java lambda。请您进一步解释一下@JeffreyBosboom好吗?我有反射方面的经验,但除了使用lambda表达式之外,我并没有太多使用它们。谢谢:)您有一个MethodHandle
h
,您需要一个EventHandler
对象,该对象调用h.invokeExact
将事件作为参数传递。所以写EventHandler=(事件)->h.invokeExact(事件)代码>。(好的,您需要一个try/catch块,因为invokeExact抛出Throwable
)javac
将在这里发出一个元工厂调用来创建lambda——您不需要手动执行。如果您不编写编译器,几乎肯定是找错了地方。如果您只是想调用该方法,不用麻烦用功能接口包装MH——只需调用MH!你让自己的生活比你需要的更艰难。嘿,哥们,我需要你的名字,这样我就可以在你之后叫我的第一个孩子了!哈哈。我想知道为什么方法签名需要一个实例,我认为这可能是问题所在,并尝试删除参数,但每次都收到一个错误,我的代码现在工作完美无瑕,我会在代码中留下评论,所以当我公开发布时,它会有一个感谢。非常感谢!:)