Pomimo przeczytania całej posiadanej dokumentacji, nie mogę rozwiązać problemu z użyciem lambdas do wykonania metody. Aby dać odrobinę tła, moim przypadkiem użycia jest system wtyczek. Używam adnotacji (@EventHandle), którą można przypisać do dowolnej metody. Używam refleksji i iteruję każdą metodę w klasie i sprawdzam, czy ma ona adnotację, jeśli metoda jest dodawana do obiektu obsługi (który jest dodawany do listy w celu przetworzenia każdego "zaznaczenia"). Oto moja klasa obsługi:Jak powinienem używać LambdaMetaFactory w moim przypadku użycia?
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);
}
}
W bieżącej iteracji tej klasie mam odlewania prosto do funkcjonalnego interfejsu EventHandler, źródło:
package me.b3nw.dev.Events;
@FunctionalInterface
public interface EventHandler {
boolean handle(GameEvent evt);
}
Obecnie pojawia się następujący błąd:
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]
Polecenie gamemodeHandler wywołuje metodę invoke w klasie Handler.
Więc wyjść to się AbstractMethodError kiedy rzucam prosto do EventHandler i wykonać, kiedy nie rzucają mi się inny błąd, który jest:
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]
Zmodyfikowany Handler, aby odzwierciedlić zmiany:
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();
}
}
Ta klasa ma metodę, która jest uwagami i powinien realizować podpis funkcjonalnego interfejsu .. ale wyraźnie nie :(Oto klasa:
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;
}
}
Jak mam to naprawić, czy używam tutaj lambdas zupełnie nie tak?
Dziękuję.
Dlaczego używasz LambdaMetafactory? Wygląda na to, że możesz użyć rzeczywistej lambdy Java, która wywołuje invokeExact. –
czy możesz wyjaśnić, że dalej @JeffreyBosboom, proszę? Mam doświadczenie z refleksją, ale poza używaniem wyrażenia lambda, nie używałem ich zbyt wiele. Dzięki :) – B3NW
Masz 'h' MethodHandle, chcesz obiekt' EventHandler', który wywołuje 'h.invokeExact' przekazując zdarzenie jako argument. Napisz więc 'EventHandler handler = (event) -> h.invokeExact (event);'. (Cóż, potrzebujesz bloku try/catch, ponieważ invokeExact 'throws Throwable'.)' Javac' będzie emitował wywołanie metafactory, aby utworzyć lambdę - nie musisz tego robić ręcznie. –