Типы В LambdaMetaFactory
я получаю исключение, когда я называю metafactory. Он говорит:
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
Я не понимаю все из документации LambdaMetafactory.metafactory. У меня проблемы с определением правильных параметров:
- MethodHandles.Поиск абонента -- это просто
- строка invokedName -- я совершенно уверен здесь
- MethodType invokedType -- что это?
- MethodType samMethodType -- err... не уверен, что здесь
- MethodHandle implMethod -- это нормально
- MethodType instantiatedMethodType -- что это? второй раз?
Итак, это сводится к тому, каковы различия между:
- MethodType invokedType
- MethodType samMethodType
- MethodType instantiatedMethodType
мой код выглядит так:
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);
}
}
}
тесты
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();
}
1 ответов
первые три параметра не являются специальными для лямбда-выражений, но стандартные аргументы для методы начальной загрузки of invokedynamic инструкция. The lookup параметр инкапсулирует контекст вызывающего абонента,invokedName и invokedType параметры представляют имя и тип invokedynamic инструкция.
это зависит от метода bootstrap, чтобы назначить больше семантики. Поскольку в этом контексте целью этой инструкции является создание экземпляра лямбда-выражения, она будет потреблять захваченные значения и производить interface экземпляра. Так что invokedType будут иметь типы параметров, отражающие тип захваченных значений или быть без параметров для не-захвата лямбд и иметь тип возврата, соответствующий желаемому функционального интерфейса. The invokedName используется для указания имени метода функционального интерфейса, что необычно, поскольку оно фактически не вызывается здесь, но поскольку вызываемое имя не имеет другого значения в противном случае, этот параметр используется повторно здесь.
на samMethodType является сигнатурой метода функционального интерфейса для реализации (на уровне байтового кода), который идентичен instantiatedMethodType пока, например, дженерики не участвуют. В противном случае, samMethodType будет подлежать стиранию типа, тогда как instantiatedMethodType включает фактические аргументы типа, например, для реализации Function<String,Integer>
-
invokedTypeбудет иметь тип возвратаFunction -
samMethodTypeбудет(Object)Object -
instantiatedMethodTypeбудет(String)Integer
обратите внимание, что для вашего конкретного случая типы в основном правильные, но так как вы хотите вызвать целевой метод на предоставленном process экземпляр, вы должны привязать его к экземпляру lambda (вы даже не пытались). К сожалению, вы не дали понять, какая у вас фактическая проблема (т. е. что вы получаете LambdaConversionException) в вашем вопросе, поэтому я не заметил проблемы раньше.
как сказано выше,invokedType должны содержит типы значений для записи в качестве типов параметров. Затем вы должны пройти фактический process экземпляр invoke звонок. Как следует из названия, invokedType должен соответствовать типу 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);
}
}