5

I was trying to check if it is possible to use MethodHandle::invoke or MethodHandle::invokeExact as method references for a functional interface that accepts a MethodHandle and returns a generified output.

(I know that invoke and invokeExact are signature polymorphic, hence the metafactory call in InvokeExact. However, I wanted to know if the compiler is able to elide the things that I had to do to derive a suitable version of 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));
  }
}

Result

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 

The good news is that the metafactory approach was able to synthesize a working functional interface instance (as printed: invoke.InvokeExact0$$Lambda$1/1878246837@1be6f5c3). The bad news is that the method reference approach resulted in a LambdaConversionException, which in turn resulted in a BootstrapMethodError.

I would then like to ask how I am supposed to interpret the error details in LambdaConversionException, since the metafactory workaround exists anyway.

4

1 回答 1

9

您手动调用的代码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();

按预期工作。只要您想要不同的返回类型,您就需要一个 lambda 表达式来代替方法引用,如

InvokeExact0<String> ie=mh -> (String)mh.invokeExact();
于 2014-12-10T10:37:13.010 回答