BootstrapMethodError, вызванный LambdaConversionException, вызванный использованием MethodHandle::invokeExact в качестве ссылки на метод

Я пытался проверить, можно ли использовать MethodHandle::invoke или MethodHandle::invokeExact в качестве ссылок на методы для функционального интерфейса, который принимает MethodHandle и возвращает сгенерированный вывод.

(Я знаю, что invoke и invokeExact являются полиморфными сигнатурами, отсюда и вызов метафабрики в InvokeExact. Однако я хотел знать, может ли компилятор исключить то, что мне нужно было сделать, чтобы получить подходящую версию invoke/invokeExact. .)

invoke.InvokeExact0

package invoke;

import java.lang.invoke.MethodHandle;

import static java.lang.System.out;
import static java.lang.invoke.LambdaMetafactory.metafactory;
import static java.lang.invoke.MethodHandles.lookup;
import static java.lang.invoke.MethodType.methodType;

@FunctionalInterface
public interface InvokeExact0<OUTPUT> {
  public OUTPUT invokeExact(MethodHandle methodHandle) throws Throwable;

  public static <OUTPUT> InvokeExact0<OUTPUT> new_(InvokeExact0<OUTPUT> invokeExact) {
    return invokeExact;
  }

  public static void main(String... arguments) throws Throwable {
    out.println(
      (InvokeExact0<String>) metafactory(
        lookup(),
        "invokeExact",
        methodType(InvokeExact0.class),
        methodType(
          Object.class,
          MethodHandle.class
        ),
        lookup().findVirtual(
          MethodHandle.class,
          "invokeExact",
          methodType(String.class)
        ),
        methodType(
          String.class,
          MethodHandle.class
        )
      )
        .getTarget()
        .invokeExact()
    );
    out.println(InvokeExact0.new_(MethodHandle::invokeExact));
  }
}

Результат

invoke.InvokeExact0$$Lambda$1/1878246837@5ca881b5                                                                                                                         
Exception in thread "main" java.lang.BootstrapMethodError: call site initialization exception                                                                             
        at java.lang.invoke.CallSite.makeSite(CallSite.java:328)                                                                                                          
        at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:296)                                                                                
        at invoke.InvokeExact0.main(InvokeExact0.java:41)                                                                                                                 
Caused by: java.lang.invoke.LambdaConversionException: Incorrect number of parameters for instance method invokeVirtual java.lang.invoke.MethodHandle.invokeExact:(MethodH
andle)Object; 0 captured parameters, 1 functional interface method parameters, 1 implementation parameters                                                                
        at java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:193)                                     
        at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:303)                                                                                     
        at java.lang.invoke.CallSite.makeSite(CallSite.java:289)                                                                                                          
        ... 2 more 

Хорошая новость заключается в том, что с помощью метафабрики удалось синтезировать работающий экземпляр функционального интерфейса (в напечатанном виде: invoke.InvokeExact0$$Lambda$1/1878246837@1be6f5c3). Плохая новость заключается в том, что подход со ссылкой на метод привел к LambdaConversionException, что, в свою очередь, привело к BootstrapMethodError.

Затем я хотел бы спросить, как я должен интерпретировать сведения об ошибке в LambdaConversionException, поскольку обходной путь метафабрики все равно существует.


person srborlongan    schedule 10.12.2014    source источник


Ответы (1)


Ваш код, вызывающий metafactory вручную, действительно показывает, что метафабрика выполнит свою работу, если дескриптор метода MethodHandle.invokeExact имеет правильную подпись. Отладка показала, что во втором случае дескриптор метода имеет сигнатуру (MethodHandle,MethodHandle)Object вместо (MethodHandle)Object.

В то время как оба могут быть созданы без проблем, так как MethodHandle.invokeExact допускает произвольные подписи (ну, первый аргумент должен быть MethodHandle, конечно), метафабрика отклоняет дескриптор, потому что он не соответствует функциональной сигнатуре, поскольку в области видимости нет дескриптора второго метода. .

Это указывает на ошибку в компиляторе, которая сгенерировала константу дескриптора метода. Как правило, если у вас есть неотражающий код, и InvokeExact0.new_(MethodHandle::invokeExact) ссылается на рефлексивную операцию, но не выполняет рефлексивную операцию, но получает ошибку времени выполнения, это указывает на компилятор. ошибка.

Есть простой обходной путь. Пока

InvokeExact0<Object> ie=MethodHandle::invokeExact;

терпит неудачу с указанной ошибкой,

InvokeExact0<Object> ie=mh -> mh.invokeExact();

работает как положено. В любом случае вам понадобится лямбда-выражение вместо ссылки на метод, как только вам понадобятся разные типы возвращаемого значения, как в

InvokeExact0<String> ie=mh -> (String)mh.invokeExact();
person Holger    schedule 10.12.2014