0

I'm trying to transform a Method (read from a file) into a lambda expression, so I can measure the time needed to execute that Method ignoring the slow Method.invoke(...) function.

I've been trying to accomplish my objective using LambdaMetafactory, but to be honest I have read so many questions and explanations about how to do this that I don't even know what I'm doing anymore.

Assuming that parameters and the method are well constructed and (here's the tricky part) that I have to deal dynamically with multiple options involving different number and types of parameters:

Object[] parameters = ...;
Method metodin = anotherClass.getMethod();

And the interface (sorry for the name, I'll change it later):

@FunctionalInterface
interface loadedMethod {
    Object execute(Object[] params);
}

Here's the code that tries to execute the method using a lambda:

MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.unreflect(metodin);

CallSite callsite = LambdaMetafactory.metafactory(
       lookup,
       "execute",
       MethodType.methodType(loadedMethod.class),
       mh.type(),
       mh,
       mh.type()
);
loadedMethod loadedMethod = (loadedMethod) callsite.getTarget().invokeExact();
System.out.println("OUTPUT: " + loadedMethod.execute(parameters)); //Error occurs here

I've read this post and I don't understand what I'm doing fine and wrong. Can you help me please?

[EDIT] The current error is:

java.lang.invoke.LambdaConversionException: Incorrect number of parameters for static method invokeStatic Example.myFunction:(int[],int,int)int; 1 captured parameters, 3 functional interface method parameters, 3 implementation parameters
    at java.base/java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:214)
    at java.base/java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:328)
    at optimex.ObjectCallable.call(ObjectCallable.java:60)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)
  • 1
    Does it work? Do you get an exception? Or does the JVM crash? Please [edit] your question to add those important details. – Johannes Kuhn Sep 15 '20 at 21:46

1 Answers1

1

Ahh, yeah.

You call metalambdafactory wrong:

The concept of the meta lambda factory is that it creates a lambda factory.
You then use the factory to create the actual instance of the lambda.

The first MethodType is for the factory - in your case you said "capture an Object[].
You probably don't want to capture anything - so the right MethodType is methodType(loadedMethod.class).

CallSite callsite = LambdaMetafactory.metafactory(
       lookup,
       "execute",
       MethodType.methodType(loadedMethod.class),
       mh.type(),
       mh,
       mh.type()
);

You invoke the resulting MethodHandle with invokeWithArguments.
As the factory doesn't need the additional arguments, you should not pass them:

loadedMethod loadedMethod = (loadedMethod) callsite.getTarget().invokeExact();
Johannes Kuhn
  • 14,778
  • 4
  • 49
  • 73
  • Looks like now it's building correcty! But now I'm getting another error when I try to call `loadedMethod.execute(parameters)` that says `java.lang.AbstractMethodError: Receiver class myclass.ObjectCallable$$Lambda$221/0x000000010038e440 does not define or inherit an implementation of the resolved method abstract execute([Ljava/lang/Object;)I of interface myclass.loadedMethod.` How am I supposted to pass the arguments of the Method to the lambda if its not this way? – Ángel Díaz de Vivar Sep 16 '20 at 13:18
  • Ahh, pass "execute" as second parameter to `metaFactory`. – Johannes Kuhn Sep 16 '20 at 14:35
  • I get the exact same error passing "execute" as the second parameter in metaFactory :/ – Ángel Díaz de Vivar Sep 16 '20 at 15:25
  • 2
    Check that the target method’s return type matches the function type (i.e. it must be `int`). – Holger Sep 17 '20 at 15:52
  • Also, all it's arguments: The signature of the method you call should be `int foo(Object[] bar)`. – Johannes Kuhn Sep 17 '20 at 16:31
  • I'm sorry, I've been bussy these days. The thing is that I don't know the arguments or return types because I'm reading a generic Method. Unless I create an interface dinamically, I don't know how to define the signature of the method. (By the way, I have edited the question in order to match the current state of the code) – Ángel Díaz de Vivar Sep 26 '20 at 18:43