我应该如何在我的用例中使用LambdaMetaFactory? [英] How should I be using LambdaMetaFactory in my use case?

查看:129
本文介绍了我应该如何在我的用例中使用LambdaMetaFactory?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Despite having read all the documentation I'm aware of, I cannot resolve an issue with using lambdas to execute a method. To give a bit of background my use case is a plugin system. I'm using an annotation (@EventHandle) which can be assigned to any method. I use reflection and iterate through every method in the class and check if it has the annotation, if it does the method is added to a handler object (which is added to a list for processing every "tick"). Here is my handler class:

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,来源:

In the current iteration of this class I'm casting straight to the functional interface 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只调用Handler类中的invoke方法。

GamemodeHandler just calls the invoke method in Handler class.

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

So it outputs an AbstractMethodError when I cast straight to EventHandler and execute, when I don't cast it I get a different error which is:

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更改:

The modified Handler to reflect changes:

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

}

此类有一个方法是注释并且应该实现功能界面的签名但是......显然不是:(这是类:

This class has a method which is annotated and should implement the signature of the functional interface but.. clearly not :( Here's the class:

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完全错了吗?

How do I go about fixing this, am I using lambdas completely wrong here?

谢谢。

推荐答案

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

If you looked at your log output you noticed that your target method signature looks like (Lme/b3nw/dev/Events/Vanilla;Lme/b3nw/dev/Events/GameEvent;)Z, in other words, since your target method is an instance method, it needs an instance of its class (i.e. Vanilla) as first argument.

如果你没有在lambda创建时提供实例但是传递了目标方法的签名作为功能签名,创建的lambda实例将有一个方法,如

If you don’t provide an instance at lambda creation time but pass the target method’s signature as functional signature, the created lambda instance will have a method like

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

与真正的界面不匹配方法

boolean handle(GameEvent evt);

。因此,您得到 AbstractMethodError LambdaMetaFactory 首先用于编译器生成的代码,并且不执行昂贵的检查,即不尝试确定将其与提供的签名进行比较的功能接口方法。

which you are trying to invoke. Therefore you get an AbstractMethodError. The LambdaMetaFactory is for compiler generated code in the first place and doesn’t perform expensive checks, i.e. doesn’t try to determine the functional interface method to compare it with the provided signature.

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

So what you have to do is to provide the instance on which the target method ought to be invoked:

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

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

Of course, you also have to adapt the code which gathers the annotated methods to pass through the instance which you are working on.

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

Note that this all refers to your first version. I couldn’t get the point of your "modified Handler".

这篇关于我应该如何在我的用例中使用LambdaMetaFactory?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆