Типы В 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.Поиск абонента -- это просто
  • строка 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();

}

1 ответов


первые три параметра не являются специальными для лямбда-выражений, но стандартные аргументы для методы начальной загрузки of invokedynamic инструкция. The lookup параметр инкапсулирует контекст вызывающего абонента,invokedName и invokedType параметры представляют имя и тип invokedynamic инструкция.

это зависит от метода bootstrap, чтобы назначить больше семантики. Поскольку в этом контексте целью этой инструкции является создание экземпляра лямбда-выражения, она будет потреблять захваченные значения и производить interface экземпляра. Так что invokedType будут иметь типы параметров, отражающие тип захваченных значений или быть без параметров для не-захвата лямбд и иметь тип возврата, соответствующий желаемому функционального интерфейса. The 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);
      }
}