Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/365.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 我应该如何在我的用例中使用LambdaMetaFactory?_Java_Reflection_Lambda_Java 8 - Fatal编程技术网

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

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

尽管阅读了我所知道的所有文档,但我无法解决使用lambdas执行方法的问题。为了给大家一点背景知识,我的用例是一个插件系统。我使用的注释(@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);
    }

}
在这个类的当前迭代中,我将直接转换到函数接口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!你让自己的生活比你需要的更艰难。嘿,哥们,我需要你的名字,这样我就可以在你之后叫我的第一个孩子了!哈哈。我想知道为什么方法签名需要一个实例,我认为这可能是问题所在,并尝试删除参数,但每次都收到一个错误,我的代码现在工作完美无瑕,我会在代码中留下评论,所以当我公开发布时,它会有一个感谢。非常感谢!:)