2015-06-07 11 views
5

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ę.

+0

Dlaczego używasz LambdaMetafactory? Wygląda na to, że możesz użyć rzeczywistej lambdy Java, która wywołuje invokeExact. –

+0

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

+2

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. –

Odpowiedz

3

Jeśli spojrzysz na swój wynik w dzienniku, zauważyłeś, że twoja metryka docelowa wygląda na (Lme/b3nw/dev/Events/Vanilla;Lme/b3nw/dev/Events/GameEvent;)Z, innymi słowy, ponieważ twoja metoda docelowa jest metodą instancji, to wymaga ona wystąpienia jej klasy (tj. Vanilla) jako pierwszego argumentu.

Jeśli nie przewidują wystąpienie w czasie tworzenia lambda ale przechodzą podpis metody docelowej jako podpisu funkcjonalnej, utworzony instancja lambda będzie mieć metody takie jak

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

która nie odpowiada faktycznemu interface metoda

boolean handle(GameEvent evt); 

, którą próbujesz wywołać. Dlatego otrzymujesz AbstractMethodError. Kod LambdaMetaFactory jest przede wszystkim generowany przez kompilator i nie przeprowadza kosztownych sprawdzeń, tj. Nie próbuje ustalić interfejsu funkcjonalnego w celu porównania go z dostarczoną sygnaturą.

Więc co trzeba zrobić, to zapewnić wystąpienie, w którym metoda docelowa powinna zostać wywołana:

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

Oczywiście, trzeba też dostosować kod który gromadzi adnotacją metod, aby przejść instancja, nad którą pracujesz.

Pamiętaj, że to wszystko odnosi się do pierwszej wersji. Nie mogłem zrozumieć twojego "zmodyfikowanego Handlera".

+0

Hej, koleś, potrzebuję twojego imienia, więc mogę nazwać się moim pierwszym urodzonym po tobie! Ha ha. Zastanawiałem się, dlaczego podpis metody potrzebował instancji, myślałem, że to może być problem i próbowałam upuszczać parametry, ale za każdym razem otrzymywałam błąd, mój kod działa teraz bezbłędnie, zostawię komentarz w kodzie, więc kiedy publicznie go udostępnię dziękuję. Bardzo doceniane! :) – B3NW

Powiązane problemy