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

标签 java reflection lambda java-8

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

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);
    }

}

在这个类的当前迭代中,我直接转换为功能接口(interface) EventHandler,来源:

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 只是调用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]

修改后的处理程序以反射(reflect)更改:

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();
    }

}

这个类有一个被注解的方法,应该实现功能接口(interface)的签名但是..显然不是:(这是这个类:

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;
    }

}

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

谢谢。

最佳答案

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

如果您在创建 lambda 时不提供实例,而是将目标方法的签名作为函数签名传递,则创建的 lambda 实例将具有如下方法

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

与真正的接口(interface)方法不匹配

boolean handle(GameEvent evt);

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

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

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);
}

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

请注意,这都是指您的第一个版本。我无法理解您的“修改后的处理程序”的意思。

关于java - 我应该如何在我的用例中使用 LambdaMetaFactory?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30695742/

相关文章:

Java线程压力测试

java - 如何在java中减小多部分文件的大小

java - 反射调用PowerManager类的setPowerSaveMode

java - 什么是反射,它为什么有用?

c++ - 如何在 C++14 中衰减右值引用类型,同时保留左值引用?

java - 在 Eclipse 中调试(Java);中断查看值时不能将鼠标悬停在变量上

java - Apache Ant 安装错误的解决方法

c# - 使用 Activator 创建实例

Java8 类型通用删除方法签名和 lambda 不起作用

python - 相当于 Python 中的 Lambda 表达式