I get an exception when I call metafactory
. It says:
java.lang.invoke.LambdaConversionException:
Incorrect number of parameters for instance met
The first three parameters are not special to lambda expressions, but standard arguments to bootstrap methods of invokedynamic
instruction. The lookup
parameter encapsulates the caller’s context, the invokedName
and invokedType
parameters represent the name and type of the invokedynamic
instruction.
It’s up to the bootstrap method to assign more semantic do it. Since in this context, the purpose of this instruction is produce a lambda expression instance, it will consume captured values and produce an interface
instance. So the invokedType
will have parameter types reflecting the type of captured values or be parameter-less for non-capturing lambdas and have a return type matching the desired functional interface. The invokedName
is used to specify the functional interface’s method name, which is unusual as it’s not actually invoked here, but since the invoked name has no other meaning otherwise, this parameter is reused here.
The samMethodType
is the signature of the functional interface’s method to implement (on the byte code level), which is identical to instantiatedMethodType
as long as, e.g. Generics is not involved. Otherwise, samMethodType
will be subject to type erasure whereas instantiatedMethodType
incorporates the actual type arguments, e.g. to implement a Function
invokedType
will have a return type of Function
samMethodType
will be (Object)Object
instantiatedMethodType
will be (String)Integer
Note that for your specific case, the types are basically correct, but since you want to invoke the target method on the provided process
instance, you have to bind it to the lambda instance (you didn’t even try). Unfortunately, you didn’t make clear what kind of actual problem you have (i.e. that you are getting a LambdaConversionException
) in your question, so I didn’t notice the problem before.
As said above, the invokedType
must contain the types of the values to capture as parameter types. Then, you have to pass the actual process
instance to the invoke
call. As the name suggests, invokedType
must match the type of invoke
:
public Step getMethodFromStepid(ProcessBase process, int stepid) {
try {
// standard reflection stuff
final String mname = "step_"+stepid;
final Method method = process.getClass().getMethod(mname);
// new java8 method reference stuff
final MethodType type=MethodType.methodType(Boolean.class);
// invokedType: bind process, generate Step
final MethodType stepType=MethodType.methodType(Step.class,process.getClass());
final MethodHandles.Lookup caller = MethodHandles.lookup();
final MethodHandle unreflect = caller.unreflect(method);
final CallSite site = LambdaMetafactory.metafactory(
caller, "apply", stepType, type, unreflect, type);
// convert site to my method reference
final MethodHandle factory = site.getTarget();
// pass the value to bind and get the functional interface instance
final Step step = (Step)factory.invoke(process);
return step;
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}