LambdaMetaFactory中的类型 [英] Types in a LambdaMetaFactory

查看:139
本文介绍了LambdaMetaFactory中的类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

调用metafactory时出现异常.它说:

I get an exception when I call metafactory. It says:

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的文档.我在找出正确的参数时遇到了问题:

I do not understand all from the documentation of LambdaMetafactory.metafactory. I have problems figuring out the correct parameters:

  • MethodHandles.Lookup调用者-如此简单
  • 字符串invokedName -我在这里相当确定
  • MethodType invokedType -这是什么?
  • MethodType samMethodType -错误... 不确定此处
  • MethodHandle implMethod -很好
  • MethodType实例化的MethodType -这又是什么?第二次?
  • MethodHandles.Lookup caller -- thats easy
  • String invokedName -- I am fairly certain here
  • MethodType invokedType -- whats this?
  • MethodType samMethodType -- err... not sure here
  • MethodHandle implMethod -- that's fine
  • MethodType instantiatedMethodType -- whats this, again? Second time?

因此归结为以下两者之间的区别:

So it boils down to what are the differences between:

  • MethodType invokedType
  • MethodType samMethodType
  • MethodType实例化的MethodType
  • 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指令的 bootstrap方法的标准参数. lookup参数封装了调用者的上下文,invokedNameinvokedType参数表示invokedynamic指令的名称和类型.

The first three parameters are not special to lambda expressions, but standard arguments to bootstrap methods of invokedynamic instruction. The lookup parameter encapsulates the caller’s context, the invokedName and invokedType parameters represent the name and type of the invokedynamic instruction.

由bootstrap方法分配更多的语义来做到这一点.由于在这种情况下,该指令的目的是产生一个lambda表达式实例,因此它将消耗捕获的值并产生一个interface实例.因此,invokedType的参数类型将反映捕获的值的类型,对于非捕获的lambda,其参数类型将为无参数,并且返回类型与所需的功能接口相匹配. invokedName用于指定功能接口的方法名称,这很不寻常,因为它实际上未在此处调用,但是由于调用的名称没有其他含义,因此此参数在此处被重用.

It’s up to the bootstrap method to assign more semantic do it. Since in this context, the purpose of this instruction is produce a lambda expression instance, it will consume captured values and produce an interface instance. So the invokedType will have parameter types reflecting the type of captured values or be parameter-less for non-capturing lambdas and have a return type matching the desired functional interface. The invokedName is used to specify the functional interface’s method name, which is unusual as it’s not actually invoked here, but since the invoked name has no other meaning otherwise, this parameter is reused here.

samMethodType是功能接口要实现的方法的签名(在字节码级别),与instantiatedMethodType相同,例如,不涉及泛型.否则,samMethodType会被类型擦除,而instantiatedMethodType会包含实际的类型参数,例如实施Function<String,Integer>

The samMethodType is the signature of the functional interface’s method to implement (on the byte code level), which is identical to instantiatedMethodType as long as, e.g. Generics is not involved. Otherwise, samMethodType will be subject to type erasure whereas instantiatedMethodType incorporates the actual type arguments, e.g. to implement a Function<String,Integer>

  • invokedType的返回类型为Function
  • samMethodType将是(Object)Object
  • instantiatedMethodType将是(String)Integer
  • invokedType will have a return type of Function
  • samMethodType will be (Object)Object
  • instantiatedMethodType will be (String)Integer

请注意,对于您的特定情况,类型基本上是正确的,但是由于您要在提供的process实例上调用target方法,因此必须将其绑定到lambda实例(您甚至没有尝试过) .不幸的是,您没有清楚问题中遇到的是哪种实际问题(即您正在得到LambdaConversionException),因此我之前没有注意到该问题.

Note that for your specific case, the types are basically correct, but since you want to invoke the target method on the provided process instance, you have to bind it to the lambda instance (you didn’t even try). Unfortunately, you didn’t make clear what kind of actual problem you have (i.e. that you are getting a LambdaConversionException) in your question, so I didn’t notice the problem before.

如上所述,invokedType必须包含要捕获为参数类型的值的类型.然后,您必须将实际的process实例传递给invoke调用.顾名思义,invokedType必须与invoke的类型匹配:

As said above, the invokedType must contain the types of the values to capture as parameter types. Then, you have to pass the actual process instance to the invoke call. As the name suggests, invokedType must match the type of 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);
      }
}

这篇关于LambdaMetaFactory中的类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆