LambdaMetaFactory 中的类型
Types in a 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.Lookup 来电者 -- 很简单
- String invokedName -- 我在这里相当确定
- MethodType invokedType -- 这是什么?
- MethodType samMethodType -- 错误... 这里不确定
- MethodHandle implMethod -- 没问题
- MethodType instantiatedMethodType -- 又是什么?第二次?
所以归结起来有什么区别:
- MethodType invokedType
- 方法类型 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();
}
前三个参数不是lambda表达式特有的,而是invokedynamic
指令的bootstrap方法的标准参数。 lookup
参数封装了调用者的上下文,invokedName
和invokedType
参数表示invokedynamic
指令的名称和类型。
这取决于bootstrap 方法来分配更多语义。由于在这种情况下,这条指令的目的是产生一个 lambda 表达式实例,它将消耗捕获的值并产生一个 interface
实例。因此 invokedType
将具有反映捕获值类型的参数类型,或者对于非捕获 lambda 是无参数的,并且具有与所需的 功能接口相匹配的 return 类型 . invokedName
用于指定函数式接口的方法名,这个参数不常见,因为这里并没有真正调用它,但是由于调用的名称没有其他含义,所以这里重用了这个参数。
samMethodType
是功能接口实现方法的签名(在字节码级别),它与 instantiatedMethodType
相同,例如不涉及泛型。否则,samMethodType
将被类型擦除,而 instantiatedMethodType
包含实际类型参数,例如实施 Function<String,Integer>
invokedType
将具有 return 类型的 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);
}
}
我在调用 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.Lookup 来电者 -- 很简单
- String invokedName -- 我在这里相当确定
- MethodType invokedType -- 这是什么?
- MethodType samMethodType -- 错误... 这里不确定
- MethodHandle implMethod -- 没问题
- MethodType instantiatedMethodType -- 又是什么?第二次?
所以归结起来有什么区别:
- MethodType invokedType
- 方法类型 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();
}
前三个参数不是lambda表达式特有的,而是invokedynamic
指令的bootstrap方法的标准参数。 lookup
参数封装了调用者的上下文,invokedName
和invokedType
参数表示invokedynamic
指令的名称和类型。
这取决于bootstrap 方法来分配更多语义。由于在这种情况下,这条指令的目的是产生一个 lambda 表达式实例,它将消耗捕获的值并产生一个 interface
实例。因此 invokedType
将具有反映捕获值类型的参数类型,或者对于非捕获 lambda 是无参数的,并且具有与所需的 功能接口相匹配的 return 类型 . invokedName
用于指定函数式接口的方法名,这个参数不常见,因为这里并没有真正调用它,但是由于调用的名称没有其他含义,所以这里重用了这个参数。
samMethodType
是功能接口实现方法的签名(在字节码级别),它与 instantiatedMethodType
相同,例如不涉及泛型。否则,samMethodType
将被类型擦除,而 instantiatedMethodType
包含实际类型参数,例如实施 Function<String,Integer>
invokedType
将具有 return 类型的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);
}
}