LambdaMetaFactory con aplicación concreta de tipo genérico

Lucas Hutchison:

Estoy tratando de utilizar Java LambdaMetaFactorypara implementar dinámicamente una 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) {
        // ...
    }
}

Aquí está mi intento 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();
}

Esto da el error:

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.

He probado una variedad de otras opciones para functionMethodTypey implementationMethodHandle, pero no han logrado conseguir este trabajo todavía. Además, incluso si se sustituye la RoutingContext.classreferencia con Object.class, esto no soluciona el error.

La única manera que puedo conseguir la lambda.handle(ctx)llamada se realice correctamente es cambiando HomeHandlerde modo que no se extiende Handler, por lo HomeHandler::handleestático, y el cambio RoutingContext.classa Object.class. Por extraño que todavía puedo emitir el lambda resultante Handler<RoutingContext>, a pesar de que ya no se extiende Handler.

Mis preguntas:

  1. ¿Cómo llego LambdaMetaFactorya trabajar con métodos no estáticos?

  2. Para esta clase SAM no estático HomeHandler, ¿cómo funciona esto con la asignación de instancia bajo el capó? No LambdaMetaFactorycrear una sola instancia de la implementación de la interfaz, independientemente del número de llamadas a métodos, ya que en este ejemplo hay variables no capturaron? ¿O crea una nueva instancia para cada llamada al método? O se supone que iba a crear una instancia única y pasarlo a la API de alguna manera?

  3. ¿Cómo llego LambdaMetaFactorya trabajar con métodos genéricos?

Editar: además de las grandes respuestas a continuación, me encontré con este post que explica los mecanismos que intervienen:

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

Jorn Vernee:

O se supone que iba a crear una instancia única y pasarlo a la API de alguna manera?

Si. HomeHandler::handlees un método de instancia, lo que significa que necesita una instancia para crear un envoltorio interfaz funcional, o pasar una instancia cada vez que se invoca (para el que Handlerno va a funcionar como un tipo FunctionalInterface).

Para usar una instancia capturado debe:

  • Cambio factoryMethodTypetomar también una HomeHandlerinstancia
  • El cambio functionMethodTypesea el tipo de borrado de la SAM, que tiene una Objectcomo argumento.
  • Cambiar el instantiatedMethodTypeargumento de que es el tipo de la manija método de destino sin el capturado HomeHandlerinstancia (ya que es capturado usted no necesita otra vez como un parámetro).
  • Pasar una instancia de HomeHandlera invokeExactla hora de crear la interfaz interfaz 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);

Por supuesto, ya que HomeHandlerlos implementos Handler, sólo podría utilizar la instancia capturado directamente;

new HomeHandler().handle(ctx);

O aprovechar el compilador para generar el código metafactory, que también utiliza invokedynamic, lo que significa que el CallSitedevuelto por LambdaMetafactory.metafactorysolamente se creará una vez:

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

O bien, si el tipo de interfaz funcional es estáticamente 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());

Supongo que te gusta

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