LambdaMetaFactory com a implementação concreta de tipo genérico

Luke Hutchison:

Eu estou tentando usar Java de LambdaMetaFactoryimplementar dinamicamente um lambda genérico, Handler<RoutingContext>:

public class RoutingContext {
    // ...
}

@FunctionalInterface
public interface Handler<X> {
    public void handle(X arg);
}

public class HomeHandler extends Handler<RoutingContext> {
    @Override
    public void handle(RoutingContext ctx) {
        // ...
    }
}

Aqui é minha tentativa em LambdaMetaFactory:

try {
    Class<?> homeHandlerClass = HomeHandler.class;

    Method method = homeHandlerClass.getDeclaredMethod(
            "handle", RoutingContext.class);
    Lookup lookup = MethodHandles.lookup();
    MethodHandle mh = lookup.unreflect(method);

    MethodType factoryMethodType = MethodType.methodType(Handler.class);
    MethodType functionMethodType = mh.type();
    MethodHandle implementationMethodHandle = mh;

    Handler<RoutingContext> lambda =
            (Handler<RoutingContext>) LambdaMetafactory.metafactory(
                    lookup,
                    "handle",
                    factoryMethodType, 
                    functionMethodType,
                    implementationMethodHandle,
                    implementationMethodHandle.type()) 
            .getTarget()
            .invokeExact();

    lambda.handle(ctx);

} catch (Throwable e) {
    e.printStackTrace();
}

Isto dá o erro:

java.lang.AbstractMethodError: Receiver class [...]$$Lambda$82/0x00000008001fa840
does not define or inherit an implementation of the resolved method abstract
handle(Ljava/lang/Object;)V of interface io.vertx.core.Handler.

Eu tentei uma série de outras opções para functionMethodTypee implementationMethodHandle, mas não conseguiram obter esse trabalho ainda. Além disso, mesmo se eu substituir a RoutingContext.classreferência com Object.class, isso não corrigir o erro.

A única maneira que eu posso obter a lambda.handle(ctx)chamada para ter sucesso é mudar HomeHandlerpara que ele não se estende Handler, tornando HomeHandler::handleestático, e mudando RoutingContext.classpara Object.class. Estranhamente eu ainda pode lançar o lambda resultante Handler<RoutingContext>, embora já não se estende Handler.

Minhas perguntas:

  1. Como faço para chegar LambdaMetaFactoryao trabalho com métodos não-estáticos?

  2. Para esta classe SAM não-estático HomeHandler, como isso funciona com a alocação instância sob o capô? Será que LambdaMetaFactorycriar uma única instância da implementação do interface, não importa quantas chamadas de método, uma vez que, neste exemplo, existem variáveis não capturado? Ou será que criar uma nova instância para cada chamada de método? Ou eu deveria criar uma única instância e passá-lo para a API de alguma forma?

  3. Como faço para chegar LambdaMetaFactoryao trabalho com métodos genéricos?

Edit: Além das grandes respostas abaixo, me deparei com este post explicando os mecanismos envolvidos:

https://medium.freecodecamp.org/a-faster-alternative-to-java-reflection-db6b1e48c33e

Jorn Vernee:

Ou eu deveria criar uma única instância e passá-lo para a API de alguma forma?

Sim. HomeHandler::handleé um método de instância, isso significa que você precisa de uma instância para criar um wrapper interface funcional, ou passar uma instância cada vez que você chamá-lo (para o qual Handlernão irá funcionar como um tipo FunctionalInterface).

Para usar um exemplo capturado você deve:

  • Mudança factoryMethodTypepara também dar uma HomeHandlerinstância
  • Alterar functionMethodTypea ser o tipo apagada do SAM, que leva um Objectcomo argumento.
  • Alterar o instantiatedMethodTypeargumento para ser o tipo da alça método de destino sem a capturou HomeHandlerinstância (uma vez que é capturado, você não precisa-lo novamente como um parâmetro).
  • Passar uma instância de HomeHandlera invokeExactao criar o interface de interface funcional.

-

Class<?> homeHandlerClass = HomeHandler.class;

Method method = homeHandlerClass.getDeclaredMethod(
        "handle", RoutingContext.class);
Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.unreflect(method);

MethodType factoryMethodType = MethodType.methodType(Handler.class, HomeHandler.class);
MethodType functionMethodType = MethodType.methodType(void.class, Object.class);
MethodHandle implementationMethodHandle = mh;

Handler<RoutingContext> lambda =
        (Handler<RoutingContext>) LambdaMetafactory.metafactory(
                lookup,
                "handle",
                factoryMethodType, 
                functionMethodType,
                implementationMethodHandle,
                implementationMethodHandle.type().dropParameterTypes(0, 1)) 
        .getTarget()
        .invokeExact(new HomeHandler()); // capturing instance
lambda.handle(ctx);

Claro, desde que HomeHandlerimplementos Handler, você pode simplesmente usar a instância capturados diretamente;

new HomeHandler().handle(ctx);

Ou alavancar o compilador para gerar o código metafactory, que também usa invokedynamic, o que significa que o CallSiteretornado por LambdaMetafactory.metafactorysó será criado uma vez que:

Handler<RoutingContext> lambda = new HomeHandler()::handle;
lambda.handle(ctx);

Ou, se o tipo de interface funcional é estaticamente saber:

MethodHandle theHandle = ...
Object theInstance = ...
MethodHandle adapted = theHandle.bindTo(theInstance);
Handler<RoutingContext> lambda = ctxt -> {
    try {
        adapted.invokeExact(ctxt);
    } catch (Throwable e) {
        throw new RuntimeException(e);
    }
};
lambda.handle(new RoutingContext());

Acho que você gosta

Origin http://43.154.161.224:23101/article/api/json?id=172533&siteId=1
Recomendado
Clasificación