我正在尝试检查是否可以使用MethodHandle::invoke或MethodHandle::invokeExact作为功能接口的方法引用,该功能接口接受MethodHandle并返回生成的输出。
(我知道invoke和invokeExact是签名多态的,因此是InvokeExact中的metafactory调用。但是,我想知道编译器是否能够取消为获得适当版本的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中的错误详细信息,因为无论如何都存在元工厂变通办法。
最佳答案
您手动调用metafactory
的代码确实表明,如果MethodHandle.invokeExact
的方法句柄具有正确的签名,则meta工厂将完成其工作。调试显示,在第二种情况下,方法句柄具有(MethodHandle,MethodHandle)Object
签名,应为(MethodHandle)Object
。
虽然两者都可以创建而没有问题,因为MethodHandle.invokeExact
允许任意签名(当然,它的第一个参数必须是MethodHandle
),但metafactory拒绝该句柄,因为它与功能签名不匹配,因为范围内没有第二个方法句柄。
这表明编译器中的一个错误会生成方法句柄常量。通常,如果您有非反射代码,并且InvokeExact0.new_(MethodHandle::invokeExact)
指的是反射操作,但不执行反射操作,但遇到运行时错误,则表明编译器存在错误。
有一个简单的解决方法。而
InvokeExact0<Object> ie=MethodHandle::invokeExact;
因上述错误而失败,
InvokeExact0<Object> ie=mh -> mh.invokeExact();
如预期般运作。无论如何,您都需要一个lambda表达式来代替方法引用,如下所示:
InvokeExact0<String> ie=mh -> (String)mh.invokeExact();