Третьим аргумент бутстраповского метод, который вы назвали lambdaType
, является вызывается типа из ассоциированной invokedynamic
(обычно заполняется JVM). Его семантика определяется методом начальной загрузки, а в случае LambdaMetaFactory
- специфицирует функциональный интерфейс как тип возврата (тип объекта для построения) и значения для захвата как тип параметра (тип значений, которые нужно использовать, когда построение экземпляра лямбда).
Так что для того, чтобы захватить this
, вы должны добавить тип this
к вашему вызываемому типу и передать this
в качестве аргумента invokeExact
вызова:
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;
}
}
Если вы хотите, чтобы захватить больше значения, вы должны добавить их в подпись в правильном порядке. НАПРИМЕР, чтобы захватить другую int
значения:
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;
}
}
Целевой метод будет иметь подпись, состоящую из this
типа, если не static
, а затем всех типов параметров. Значения захвата будут отображаться для типов этой подписи слева направо, а остальные типы параметров, если таковые имеются, вносят вклад в функциональную подпись, поэтому должны соответствовать типам параметров interface
.
Это означает, что при отсутствии фиксированных значений и целевом методе нет static
, тип приемника метода может ассоциироваться с первым типом функциональной сигнатуры, как в ToIntFunction<String> f=String::length;
.
Я предполагаю, что это для понимания того, как lambdas работают внутри, потому что иначе вы бы вернули новый IntSupplier() {...}; 'без магии. – immibis
Что я не понимаю, это комментарий «Не было бы виртуальным». – Holger