java - Types in a LambdaMetaFactory -
i exception when call metafactory
. says:
java.lang.invoke.lambdaconversionexception: incorrect number of parameters instance method invokevirtual my.executetest$aprocess.step_1:()boolean; 0 captured parameters, 0 functional interface method parameters, 0 implementation parameters
i not understand documentation of lambdametafactory.metafactory
. have problems figuring out correct parameters:
- methodhandles.lookup caller -- thats easy
- string invokedname -- here
- methodtype invokedtype -- whats this?
- methodtype sammethodtype -- err... not sure here
- methodhandle implmethod -- that's fine
- methodtype instantiatedmethodtype -- whats this, again? second time?
so boils down differences between:
- methodtype invokedtype
- methodtype sammethodtype
- methodtype instantiatedmethodtype
my code this:
package my; import java.lang.invoke.*; import java.lang.reflect.method; public class execute { public interface processbase {}; @functionalinterface public interface step { boolean apply(); } public step getmethodfromstepid(processbase process, int stepid) { try { // standard reflection stuff final methodhandle unreflect = caller.unreflect(method); final string mname = "step_"+stepid; // new java8 method reference stuff final method method = process.getclass().getmethod(mname); final methodtype type=methodtype.methodtype(boolean.class); final methodtype steptype=methodtype.methodtype(step.class); final methodhandles.lookup caller = methodhandles.lookup(); final callsite site = lambdametafactory.metafactory( caller, "apply", steptype, type, unreflect, type); // damn // convert site method reference final methodhandle factory = site.gettarget(); final step step = (step) factory.invoke(); return step; } catch (throwable throwable) { throw new runtimeexception(throwable); } } }
with tests
package my; import org.junit.test; import static org.junit.assert.*; public class executetest { private class aprocess implements execute.processbase { public boolean step_1() { return true; } public boolean step_2() { return false; } } @test public void getmethodfromstepid() throws exception { final aprocess process = new aprocess(); { final execute.step methodref = instance.getmethodfromstepid(process, 1); final boolean result = methodref.apply(); asserttrue(result); } { final execute.step methodref = instance.getmethodfromstepid(process, 2); final boolean result = methodref.apply(); assertfalse(result); } } private final execute instance = new execute(); }
the first 3 parameters not special lambda expressions, standard arguments bootstrap methods of invokedynamic
instruction. lookup
parameter encapsulates caller’s context, invokedname
, invokedtype
parameters represent name , type of invokedynamic
instruction.
it’s bootstrap method assign more semantic it. since in context, purpose of instruction produce lambda expression instance, consume captured values , produce interface
instance. invokedtype
have parameter types reflecting type of captured values or parameter-less non-capturing lambdas , have return type matching desired functional interface. invokedname
used specify functional interface’s method name, unusual it’s not invoked here, since invoked name has no other meaning otherwise, parameter reused here.
the sammethodtype
signature of functional interface’s method implement (on byte code level), identical instantiatedmethodtype
long as, e.g. generics not involved. otherwise, sammethodtype
subject type erasure whereas instantiatedmethodtype
incorporates actual type arguments, e.g. implement function<string,integer>
invokedtype
have return type offunction
sammethodtype
(object)object
instantiatedmethodtype
(string)integer
note specific case, types correct, since want invoke target method on provided process
instance, have bind lambda instance (you didn’t try). unfortunately, didn’t make clear kind of actual problem have (i.e. getting lambdaconversionexception
) in question, didn’t notice problem before.
as said above, invokedtype
must contain types of values capture parameter types. then, have pass actual process
instance invoke
call. name suggests, invokedtype
must match 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 method reference final methodhandle factory = site.gettarget(); // pass value bind , functional interface instance final step step = (step)factory.invoke(process); return step; } catch (throwable throwable) { throw new runtimeexception(throwable); } }
Comments
Post a Comment