LambdaMetaFactory中的类型

tow*_*owi 9 java reflection lambda java-8 method-reference

我打电话时遇到异常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
Run Code Online (Sandbox Code Playgroud)

我不明白所有的文件LambdaMetafactory.metafactory.我在确定正确的参数时遇到了问题:

  • MethodHandles.Lookup调用者 - 这很容易
  • 字符串invokedName - 我在这里相当肯定
  • MethodType invokedType - 这是什么?
  • MethodType samMethodType - 错误...... 不确定这里
  • 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);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

通过测试

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();

}
Run Code Online (Sandbox Code Playgroud)

Hol*_*ger 5

前三个参数对lambda表达式不是特殊的,而是bootstrapinvokedynamic指令方法的标准参数.该lookup参数封装了调用者的上下文,invokedNameinvokedTypeparameters 和parameters表示invokedynamic指令的名称和类型.

由引导方法决定分配更多语义.因为在此上下文中,此指令的目的是生成lambda表达式实例,它将使用捕获的值并生成interface实例.因此,invokedType将具有反映捕获值的类型的参数类型或者对于非捕获lambdas而言是无参数的,并且具有与所需功能接口匹配的返回类型.将invokedName用于指定的功能接口的方法名,因为它不是真正这里调用这是不寻常的,但因为被调用的名字有没有其他的意思,否则,这个参数在这里重复使用.

samMethodType是功能接口实现方法的签名(在字节代码级别上),instantiatedMethodType只要与Generics不相关即可.否则,samMethodType将受到类型擦除,而instantiatedMethodType包含实际类型参数,例如实现aFunction<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);
      }
}
Run Code Online (Sandbox Code Playgroud)

  • 不,您生成了一个 Step 实例,因此您的 invokedType 是 ()Step ,您已经通过 MethodType.methodType(Step.class) 正确创建了它。由于此接口不是通用的,因此 `samMethodType` 和 `instantiatedMethodType` 都是 `()Boolean`,您已经通过 `MethodType.methodType(Boolean.class)` 正确创建了它们。没有必要改变任何东西。 (2认同)
  • 不要被`(ParameterTypes)ReturnType`语法弄糊涂.使用`MethodType.methodType(ReturnType [,ParameterTypes])`时必须更改顺序,而不是对没有参数的签名很重要.或者你使用`MethodType.fromMethodDescriptorString("()Ljava/lang/Boolean;")`,它遵循`(ParameterTypes)ReturnType`语法,但它不会使代码更具可读性. (2认同)