I get an exception when I call metafactory. It says:
java.lang.invoke.LambdaConversionException:
Incorrect number of parameters for instance method
invokeVirtual my.ExecuteTest$AProcess.step_1:()Boolean;
0 captured parameters,
0 functional interface method parameters,
0 implementation parameters
I do not understand all from the documentation of LambdaMetafactory.metafactory. I have problems figuring out the correct parameters:
MethodHandles.Lookup caller -- thats easy
String invokedName -- I am fairly certain here
MethodType invokedType -- whats this?
MethodType samMethodType -- err... not sure here
MethodHandle implMethod -- that's fine
MethodType instantiatedMethodType -- whats this, again? Second time?
So it boils down to what are the differences between:
MethodType invokedType
MethodType samMethodType
MethodType instantiatedMethodType
My code is like 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 to my method reference
final MethodHandle factory = site.getTarget();
final Step step = (Step) factory.invoke();
return step;
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}
}
with the 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 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<String,Integer>
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);
}
}
Related
I have a few classes that each implement an interface. From these classes I search out a method using an annotation. This method returns a boolean and always has an object as parameter, which always inherits from another fixed object. Now I want to create a functional interface from this method. Optimally of course a Predicate, which takes over the mentioned parameter. This I have now tried for some time to implement with LambdaMetafactory:
private Predicate<Parent> toPredicate(final ExampleInterface instance, final Method method) {
try {
final MethodHandle handle = LOOKUP.unreflect(method);
final MethodType signature = MethodType.methodType(boolean.class, method.getParameters()[0].getType());
final CallSite callSite = LambdaMetafactory.metafactory(
LOOKUP,
"test",
MethodType.methodType(Predicate.class, instance.getClass()),
signature,
handle,
signature
);
return (Predicate<Parent>) callSite.getTarget().invoke(instance);
} catch (Throwable e) {
e.printStackTrace();
return null;
}
}
My problem now is that when I call the Predicate's test method, an AbstractMethodError is thrown. When I use signature with (boolean.class, Parent.class) I get a LambdaConversionException. Is it even possible to implement it that dynamically? If yes, how?
Since you know the target interface type is Predicate you could use a lambda expression as well:
private Predicate<Parent> toPredicate(final ExampleInterface instance, final Method method) {
final MethodHandle handle = LOOKUP.unreflect(method)
.bindTo(instance)
.asType(MethodType.methodType(boolean.class, Parent.class);
return parent -> {
try {
return (boolean) handle.invokeExact(parent);
} catch (Throwable t) {
throw new RuntimeException("Should not happen", t);
}
};
}
Using LambdaMetafactory is only really beneficial in specific cases where a particular call site only sees a single instance of the interface instance. Also note that every time you create a metafactory, a new class is generated (unless it can be loaded from the CDS archive), which has its own costs associated with it.
The thing you're getting wrong is the interface method type. It should be the erasure of the test method in Predicate:
final MethodHandle handle = LOOKUP.unreflect(method);
final CallSite callSite = LambdaMetafactory.metafactory(
LOOKUP,
"test",
MethodType.methodType(Predicate.class, instance.getClass()),
MethodType.methodType(boolean.class, Object.class),
handle,
MethodType.methodType(boolean.class, Parent.class)
);
I'm trying to dynamically create a method reference of type BiConsumer through LambdaMetafactory.
I was trying to apply two approaches found on https://www.cuba-platform.com/blog/think-twice-before-using-reflection/ - createVoidHandlerLambda and here Create BiConsumer as Field setter without reflection the Holger's answer.
However in both cases I'm having below error:
Exception in thread "main" java.lang.AbstractMethodError: Receiver class org.home.ref.App$$Lambda$15/0x0000000800066040 does not define or inherit an implementation of the resolved method abstract accept(Ljava/lang/Object;Ljava/lang/Object;)V of interface java.util.function.BiConsumer.
at org.home.ref.App.main(App.java:20)
My code is something like this:
public class App {
public static void main(String[] args) throws Throwable {
MyClass myClass = new MyClass();
BiConsumer<MyClass, Boolean> setValid = MyClass::setValid;
setValid.accept(myClass, true);
BiConsumer<MyClass, Boolean> mappingMethodReferences = createHandlerLambda(MyClass.class);
mappingMethodReferences.accept(myClass, true);
}
#SuppressWarnings("unchecked")
public static BiConsumer<MyClass, Boolean> createHandlerLambda(Class<?> classType) throws Throwable {
Method method = classType.getMethod("setValid", boolean.class);
MethodHandles.Lookup caller = MethodHandles.lookup();
CallSite site = LambdaMetafactory.metafactory(caller,
"accept",
MethodType.methodType(BiConsumer.class),
MethodType.methodType(void.class, MyClass.class, boolean.class),
caller.findVirtual(classType, method.getName(),
MethodType.methodType(void.class, method.getParameterTypes()[0])),
MethodType.methodType(void.class, classType, method.getParameterTypes()[0]));
MethodHandle factory = site.getTarget();
return (BiConsumer<MyClass, Boolean>) factory.invoke();
}
public static <C, V> BiConsumer<C, V> createSetter(Class<?> classType) throws Throwable {
Field field = classType.getDeclaredField("valid");
MethodHandles.Lookup lookup = MethodHandles.lookup();
final MethodHandle setter = lookup.unreflectSetter(field);
final CallSite site = LambdaMetafactory.metafactory(lookup,
"accept", MethodType.methodType(BiConsumer.class, MethodHandle.class),
setter.type().erase(), MethodHandles.exactInvoker(setter.type()), setter.type());
return (BiConsumer<C, V>)site.getTarget().invokeExact(setter);
}
}
Where MyClass looks like this:
public class MyClass {
public boolean valid;
public void setValid(boolean valid) {
this.valid = valid;
System.out.println("Called setValid");
}
}
I will appreciate for help with this one.
EDIT #1.
After consulting #Holger I've modified createSetter method to:
#SuppressWarnings("unchecked")
public static <C, V> BiConsumer<C, V> createSetter(Class<?> classType) throws Throwable {
Field field = classType.getDeclaredField("valid");
MethodHandles.Lookup lookup = MethodHandles.lookup();
final MethodHandle setter = lookup.unreflectSetter(field);
MethodType type = setter.type();
if(field.getType().isPrimitive())
type = type.wrap().changeReturnType(void.class);
final CallSite site = LambdaMetafactory.metafactory(lookup,
"accept", MethodType.methodType(BiConsumer.class, MethodHandle.class),
type.erase(), MethodHandles.exactInvoker(setter.type()), type);
return (BiConsumer<C, V>)site.getTarget().invokeExact(setter);
}
Now this method does not throw the initial Exception althoug it seems that calling accept on this method reference has no effect. I do not see "Called setValid" in logs for this call. Only for MyClass::setValid;
Note that your use of getMethod and caller.findVirtual(…) for the same method is redundant. If your starting point is a Method, you may use unreflect, e.g.
Method method = classType.getMethod("setValid", boolean.class);
MethodHandles.Lookup caller = MethodHandles.lookup();
MethodHandle target = caller.unreflect(method);
This might be useful when you discover methods dynamically and/or are looking for other artifacts like annotations in the process. Otherwise, just getting the MethodHandle via findVirtual is enough.
Then, you have to understand the three different function types:
The target method handle has a specific type which is given implicitly when passing the method handle to the factory. In your case, it is (MyClass,boolean) → void
The generic function type associated with the intended result type
BiConsumer<MyClass, Boolean>, which is (MyClass,Boolean) → void
The erased type of the BiConsumer interface, which is (Object,Object) → void
Only specifying all three types correctly tells the factory that it must implement the method
void accept(Object,Object) with code which will cast the first argument to MyClass and the second to Boolean, followed by unwrapping the second argument to boolean, to eventually invoke the target method.
We could specify the types explicitly, but to make the code as reusable as possible, we can call type() on the target, followed by using adapter methods.
wrap() will convert all primitive types to their wrapper type. Unfortunately, this also implies converting the return type to Void, so we have to set it back to void again.
This gives us the instantiatedMethodType parameter. (Compare with the documentation)
erase() will convert all reference types to Object but leave all primitive types as-is. So applying it to the instantiatedMethodType gives us the erased type.
It depends on the particular target interface whether this simple transformation is sufficient. For the interfaces in java.util.function, it is.
Another point to raise the reusability is to use an actual type parameter for the method receiver class, as we get the class as parameter anyway:
public static <T>
BiConsumer<T, Boolean> createHandlerLambda(Class<T> classType) throws Throwable {
MethodHandles.Lookup caller = MethodHandles.lookup();
MethodHandle target = caller.findVirtual(classType, "setValid",
MethodType.methodType(void.class, boolean.class));
MethodType instantiated = target.type().wrap().changeReturnType(void.class);
CallSite site = LambdaMetafactory.metafactory(caller,
"accept", MethodType.methodType(BiConsumer.class),
instantiated.erase(), target, instantiated);
return (BiConsumer<T, Boolean>)site.getTarget().invoke();
}
I tried to do:
public class HelloWorld {
public static void main(String... args){
final String string = "a";
final Supplier<?> supplier = string::isEmpty;
System.out.println(supplier);
}
}
I get:
HelloWorld$$Lambda$1/471910020#548c4f57
I would like to get the string isEmpty. How can I do this?
EDIT: the code of the method I created is this one:
public class EnumHelper {
private final static String values = "values";
private final static String errorTpl = "Can't find element with value `{0}` for enum {1} using getter {2}()";
public static <T extends Enum<T>, U> T getFromValue(T enumT, U value, String getter) {
#SuppressWarnings("unchecked")
final T[] elements = (T[]) ReflectionHelper.callMethod(enumT, values);
for (final T enm: elements) {
if (ReflectionHelper.callMethod(enm, getter).equals(value)) {
return enm;
}
}
throw new InvalidParameterException(MessageFormat.format(errorTpl, value, enumT, getter));
}
}
The problem is I can't pass as parameter T::getValue, since getValue is not static. And I can't pass someEnumElem::getValue, since the get() will return the value of that element. I could use inside the for loop:
Supplier<U> getterSupllier = enm:getValue;
if (getterSupllier.get().equals(value)) {
[...]
}
but in this way getValue is fixed, I can't pass it as parameter. I could use some third-party library to do an eval(), but I really don't want to open that Pandora vase :D
EDIT 2: Function does work with no parameters methods, but only in Java 11. Unluckily I'm stuck with Java 8.
string::isEmpty will be constructed by a method LambdaMetafactory.metafactory which has implMethod among its parameters.
final String methodName = implMethod.internalMemberName().getName();
would return a method name (here, "isEmpty") if we had access to the arguments passed to this factory method, and to implMethod in particular. The arguments generated by up-calls from the JVM that provides very specific information for the java.lang.invoke API.
For example, to initialise a DirectMethodHandle which string::isEmpty represents, the JVM will call the following method.
/**
* The JVM is resolving a CONSTANT_MethodHandle CP entry. And it wants our help.
* It will make an up-call to this method. (Do not change the name or signature.)
* The type argument is a Class for field requests and a MethodType for non-fields.
* <p>
* Recent versions of the JVM may also pass a resolved MemberName for the type.
* In that case, the name is ignored and may be null.
*/
static MethodHandle linkMethodHandleConstant(Class<?> callerClass, int refKind,
Class<?> defc, String name, Object type)
That name (exactly what you requested) will be put there by the JVM, and there is no means for us to access it. For now.
To read:
Explicit use of LambdaMetafactory
What are CONSTANT_MethodHandle, CONSTANT_MethodType, and CONSTANT_InvokeDynamic?
MethodHandle - What is it all about?
In short: no.
Once a method reference is used you'll have an implementation of the functional interface that you requested (Supplier<?> in this case), but basically all the specifics of that object as undefined (or implementation-defined to be precise).
The spec doesn't say anything about it being a separate object, what its toString() has to be or what else you can do with it. It's a Supplier<?> and basically nothing else.
The same thing applies to lambda expressions.
So if you had used
final Supplier<?> supplier = () -> string.isEmpty();
the Supplier would do the same thing and you also couldn't get back to the "code" of the lambda.
In short: No, it's not possible.
A workaround that I've been using is to create methods that wrap java.util.functional instances into "named" versions.
import java.util.Objects;
import java.util.function.Supplier;
public class Named {
public static void main(String[] args) {
String string = "a";
Supplier<?> supplier = string::isEmpty;
Supplier<?> named = named("isEmpty", supplier);
System.out.println(named);
}
static <T> Supplier<T> named(String name, Supplier<? extends T> delegate) {
Objects.requireNonNull(delegate, "The delegate may not be null");
return new Supplier<T>() {
#Override
public T get() {
return delegate.get();
}
#Override
public String toString() {
return name;
}
};
}
}
Of course this does not make sense for all application cases. Most importantly, it does not allow you to "derive" things like the method name of a Supplier in hindsight when you just receive it, for example, as a method argument. The reason for that is more technical, most importantly: The supplier does not have to be a method reference.
But when you control the creation of the Supplier, changing string::isEmpty to Named.named("isEmpty", string::isEmpty) can be a reasonable way to go.
In fact, I did this so systematically for all the functional types that I even considered pushing this into some publicly visible (GitHub/Maven) library...
It’s weird that you are asking about the opposite of what you actually need.
You have a method that receives a string and wants to execute a method with that name, but for some unknown reason, you ask for the opposite, to get the method name from an existing supplier.
And already written in a comment before knowing the actual code, you can solve the actual problem by replacing the String getter parameter with Function<T,U> getter.
You don’t need any Reflection tool here:
public class EnumHelper {
private final static String errorTpl
= "Can't find element with value `{0}` for enum {1} using getter {2}()";
public static <T extends Enum<T>, U> T getFromValue(
T enumT, U value, Function<? super T, ?> getter) {
final T[] elements = enumT.getDeclaringClass().getEnumConstants();
for (final T enm: elements) {
if(getter.apply(enm).equals(value)) {
return enm;
}
}
throw new IllegalArgumentException(
MessageFormat.format(errorTpl, value, enumT, getter));
}
}
The getter Function can be implemented via method reference, e.g.
ChronoUnit f = EnumHelper.getFromValue(
ChronoUnit.FOREVER, Duration.ofMinutes(60), ChronoUnit::getDuration);
System.out.println(f);
I made the signature of the function parameter more generous compared to Function<T,U>, to raise the flexibility regarding existing functions, e.g.
Function<Object,Object> func = Object::toString;
ChronoUnit f1 = EnumHelper.getFromValue(ChronoUnit.FOREVER, "Years", func);
System.out.println(f1.name());
If printing meaningful names in the erroneous case is really important, just add a name parameter just for reporting:
public static <T extends Enum<T>, U> T getFromValue(
T enumT, U value, Function<? super T, ?> getter, String getterName) {
final T[] elements = enumT.getDeclaringClass().getEnumConstants();
for (final T enm: elements) {
if(getter.apply(enm).equals(value)) {
return enm;
}
}
throw new IllegalArgumentException(
MessageFormat.format(errorTpl, value, enumT, getterName));
}
to be called like
ChronoUnit f = EnumHelper.getFromValue(
ChronoUnit.FOREVER, Duration.ofMinutes(60), ChronoUnit::getDuration, "getDuration");
That’s still better than using Reflection for the normal operations…
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? Does LambdaMetaFactory 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
Or was I supposed to create a single instance and pass it in to the API somehow?
Yes. HomeHandler::handle is an instance method, that means you need an instance to create a functional interface wrapper, or pass an instance every time you invoke it (for which Handler won't work as a FunctionalInterface type).
To use a captured instance you should:
Change factoryMethodType to also take a HomeHandler instance
Change functionMethodType to be the erased type of the SAM, which takes an Object as argument.
Change the instantiatedMethodType argument to be the type of the target method handle without the captured HomeHandler instance (since it's captured you don't need it again as a parameter).
Pass an instance of HomeHandler to invokeExact when creating the functional interface interface.
-
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);
Of course, since HomeHandler implements Handler, you could just use the captured instance directly;
new HomeHandler().handle(ctx);
Or leverage the compiler to generate the metafactory code, which also uses invokedynamic, meaning that the CallSite returned by LambdaMetafactory.metafactory will only be created once:
Handler<RoutingContext> lambda = new HomeHandler()::handle;
lambda.handle(ctx);
Or, if the functional interface type is statically know:
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());
Since you said “it's a shame the LambdaMetaFactory API is so complex”, it should be mentioned it can be done simpler.
First, when using LambdaMetaFactory, use it straight-forwardly:
Lookup lookup = MethodHandles.lookup();
MethodType fType = MethodType.methodType(void.class, RoutingContext.class);
MethodHandle mh = lookup.findVirtual(HomeHandler.class, "handle", fType);
Handler<RoutingContext> lambda = (Handler<RoutingContext>) LambdaMetafactory.metafactory(
lookup, "handle", MethodType.methodType(Handler.class, HomeHandler.class),
fType.erase(), mh, fType).getTarget().invokeExact(new HomeHandler());
You are going to invoke an instance method with a bound receiver and the target method’s type excluding the receiver is identical to the instantiatedMethodType parameter. Further, since the bound of T in Handler<T> is Object, you can simply use erase() on that method type to get the erased signature for the samMethodType parameter.
It’s not always that simple. Consider binding a method static int method(int x) to Consumer<Integer>. Then, the samMethodType parameter is (Object)void, the instantiatedMethodType parameter is (Integer)void, whereas the target method’s signature is int(int). You need all these parameters to correctly describe the code to generate. Considering that the other (first three) parameters are normally filled in by the JVM anyway, this method does already require only the necessary minimum.
Second, if you don’t need the maximum performance, you can simply use a Proxy based implementation:
MethodHandle mh = MethodHandles.lookup().findVirtual(HomeHandler.class,
"handle", MethodType.methodType(void.class, RoutingContext.class));
Handler<RoutingContext> lambda = MethodHandleProxies.asInterfaceInstance(
Handler.class, mh.bindTo(new HomeHandler()));
This option even exists since Java 7
Given this code:
MethodType mt = MethodType.methodType(void.class, DomainObject.class);
NOOP_METHOD = RULE_METHOD_LOOKUP.findVirtual(RulesEngine.class, "noOpRule", mt);
the NOOP_METHOD produced is
MethodHandle(RulesEngine,DomainObject)void
Why is that first parameter there, that causes failures when i invoke it, like
mh.invoke(domainObject);
as the error message is:
java.lang.invoke.WrongMethodTypeException: cannot convert MethodHandle(RulesEngine,DomainObject)void to (DomainObject)void
Here is the method in question:
public void noOpRule(DomainObject d) {
}
The method noOpRule is an instance method of the RulesEngine class.
To call it in regular code, you need a RulesEnigne object as well as a DomainObject object:
public static void callNoOpRule(RulesEngine rulesEngine, DomainObject domainObject) {
rulesEngine.noOpRule(domainObject);
}
To call it through the MethodHandle you need both objects as well:
mh.invoke(rulesEngine, domainObject);
or, if you are trying to invoking from an instance method of RulesEngine:
mh.invoke(this, domainObject);