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。查找调用者——这很容易
  • String 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();
}

前三个参数不是lambda表达式所特有的,而是invokedynamic指令引导方法的标准参数。lookup参数封装了调用者的上下文,invokedNameinvokedType参数表示invokedynamic指令的名称和类型。

这是由bootstrap方法分配更多的语义做它。由于在此上下文中,该指令的目的是生成一个lambda表达式实例,因此它将使用捕获的值并生成一个interface实例。因此,invokedType将具有反映捕获值类型的参数类型,或者对于非捕获lambdas是无参数的,并且具有与所需的功能接口匹配的返回类型。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);
      }
}

相关内容

  • 没有找到相关文章

最新更新