The third argument to the bootstrap method, which you named lambdaType
, is the invoked type of the associated invokedynamic
instruction (normally filled in by the JVM). It’s semantic is defined by the bootstrap method and in the case of the LambdaMetaFactory
, it specifies the functional interface as return type (the type of the object to construct) and the values to capture as parameter type (the type of the values to consume when constructing a lambda instance).
So in order to capture this
, you have to add the type of this
to your invoked type and pass this
as an argument to the invokeExact
call:
public class Test {
public static void main(String... arg) throws Throwable {
System.out.println(new Test().foo().getAsInt());
}
public IntSupplier foo() throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType methodType = MethodType.methodType(int.class),
invokedType = MethodType.methodType(IntSupplier.class, Test.class);
MethodHandle methodHandle = lookup.findVirtual(getClass(), "fortyTwo", methodType);
CallSite callSite = LambdaMetafactory.metafactory(lookup, "getAsInt",
invokedType, methodType, methodHandle, methodType);
return (IntSupplier) callSite.getTarget().invokeExact(this);
}
public int fortyTwo() {
return 42;
}
}
If you want to capture more values, you have to add them to the signature in the right order. E.g., to capture another int
value:
public class Test {
public static void main(String... arg) throws Throwable {
System.out.println(new Test().foo(100).getAsInt());
}
public IntSupplier foo(int capture) throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType methodType = MethodType.methodType(int.class, int.class),
functionType = MethodType.methodType(int.class),
invokedType = MethodType.methodType(IntSupplier.class, Test.class, int.class);
MethodHandle methodHandle=lookup.findVirtual(getClass(),"addFortyTwo",methodType);
CallSite callSite = LambdaMetafactory.metafactory(lookup, "getAsInt",
invokedType, functionType, methodHandle, functionType);
return (IntSupplier) callSite.getTarget().invokeExact(this, capture);
}
public int addFortyTwo(int valueToAdd) {
return 42+valueToAdd;
}
}
The target method will have a signature consisting of the this
type, if not static
, followed by all parameter types. The capture values will map in order to this signature’s types from left to right and the remaining parameter types, if any, contribute to the functional signature, hence have to match the interface
method’s parameter types.
This implies that when there are no captured values and the target method is not static
, the method receiver type might become associated with the first type of the functional signature, as in ToIntFunction<String> f=String::length;
.