I am trying to use Java's LambdaMetaFactory
to dynamically implement a generic lambda, 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) {
// ...
}
}
Here is my attempt at 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();
}
This gives the 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.
I have tried a range of other options for functionMethodType
and implementationMethodHandle
, but have not managed to get this working yet. Also, even if I replace the RoutingContext.class
reference with Object.class
, this does not fix the error.
The only way I can get the lambda.handle(ctx)
call to succeed is by changing HomeHandler
so that it does not extend Handler
, making HomeHandler::handle
static, and changing RoutingContext.class
to Object.class
. Oddly I can still cast the resulting lambda to Handler<RoutingContext>
, even though it no longer extends Handler
.
My questions:
How do I get
LambdaMetaFactory
to work with non-static methods?For this non-static SAM class
HomeHandler
, how does this work with instance allocation under the hood? DoesLambdaMetaFactory
create a single instance of the interface implementation, no matter how many method calls, since in this example there are no captured variables? Or does it create a new instance for each method call? Or was I supposed to create a single instance and pass it in to the API somehow?How do I get
LambdaMetaFactory
to work with generic methods?
Edit: in addition to the great answers below, I came across this blog post explaining the mechanisms involved:
https://medium.freecodecamp.org/a-faster-alternative-to-java-reflection-db6b1e48c33e