LambdaMetaFactory中的类型

时间:2016-09-22 13:33:09

标签: 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

我对LambdaMetafactory.metafactory的文档中的所有内容都不了解。我在找出正确的参数时遇到了问题:

  • MethodHandles.Lookup来电 - 很容易
  • String 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);
    }
  }
}

带有测试

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 个答案:

答案 0 :(得分:5)

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

由引导方法来分配更多语义。因为在此上下文中,此指令的目的是生成lambda表达式实例,它将使用捕获的值并生成interface实例。因此invokedType将具有反映捕获值类型的参数类型,或者对于非捕获lambda具有参数类型,并且具有与所需功能接口匹配的返回类型。 invokedName用于指定功能接口的方法名称,这是不常见的,因为它实际上并未在此处调用,但由于调用的名称没有其他含义,因此此参数在此处重复使用。

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

  • Function的返回类型为samMethodType
  • (Object)Object将为instantiatedMethodType
  • (String)Integer将为process

请注意,对于您的特定情况,类型基本上是正确的,但由于您要在提供的LambdaConversionException实例上调用目标方法,您必须将其绑定到lambda实例(您甚至没有尝试)。不幸的是,你没有弄清楚你的问题是什么样的实际问题(即你得到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); } } 的类型匹配:

  public static class ArrayExtensions
    {
        /// <summary>
        /// Removes the last element.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="array">The array.</param>
        /// <returns>T[].</returns>
        public static T[] RemoveLastElement<T>(this T[] array)
        {
            var stack = new Stack<T>(array);
            stack.Pop();
            return stack.ToArray().Reverse().ToArray();
        }

        /// <summary>
        /// Removes the first element.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="array">The array.</param>
        /// <returns>T[].</returns>
        public static T[] RemoveFirstElement<T>(this T[] array)
        {
            var queue = new Queue<T>(array);
            queue.Dequeue();
            return queue.ToArray();
        }
    }