问题描述
我试图检查是否可以使用MethodHandle :: invoke或MethodHandle :: invokeExact作为接受MethodHandle的函数接口的方法引用,并返回一个通用的输出。
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.
(我知道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
好消息是metafactory方法能够合成一个有效的功能接口实例(如打印: invoke.InvokeExact0$$Lambda $ 1 / 1878246837@1be6f5c3 的)。
坏消息是方法引用方法导致LambdaConversionException,从而导致BootstrapMethodError。
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.
我想问一下我应该如何解释LambdaConversionException中的错误细节,因为无论如何都存在metafactory解决方法。
推荐答案
您的代码手动调用 metafactory
确实表明如果 MethodHandle.invokeExact
的方法句柄有权,元工厂将完成它的工作签名。调试显示,在第二种情况下,方法句柄有一个(MethodHandle,MethodHandle)对象
签名,它应该是(MethodHandle)Object
。
Your code calling metafactory
manually indeed shows that the meta factory will do it’s job if the method handle to MethodHandle.invokeExact
has the right signature. Debugging revealed that in the second case the method handle has a (MethodHandle,MethodHandle)Object
signature where it should be (MethodHandle)Object
.
虽然两者都可以毫无问题地创建,因为 MethodHandle.invokeExact
允许任意签名(好吧,它的第一个参数必须是 MethodHandle
,当然)metafactory拒绝句柄,因为它与函数签名不匹配,因为范围内没有第二个方法句柄。
While both can be created without a problem as MethodHandle.invokeExact
allows arbitrary signatures (well, it’s first argument has to be MethodHandle
, of course) the metafactory rejects the handle because it doesn’t match the functional signature as there is no second method handle in scope.
这表示编译器中生成方法句柄常量的错误。通常,如果您有非反射代码,并且 InvokeExact0.new_(MethodHandle :: invokeExact)
将引用到反射操作但不执行反射操作,但得到运行时错误,它表示编译器错误。
This indicates a bug in the compiler which generated the method handle constant. Generally, if you have non-reflective code, and InvokeExact0.new_(MethodHandle::invokeExact)
refers to a reflective operation but doesn’t perform the reflective operation, but get a runtime error, it indicates a compiler bug.
有一个简单的解决方法。虽然
There is a simple work-around. While
InvokeExact0<Object> ie=MethodHandle::invokeExact;
失败并显示错误,
InvokeExact0<Object> ie=mh -> mh.invokeExact();
按预期工作。只要你想要不同的返回类型,就需要一个lambda表达式代替方法引用,如
works as expected. You will need a lambda expression in place of the method reference anyway as soon as you want different return types as in
InvokeExact0<String> ie=mh -> (String)mh.invokeExact();
这篇关于由使用MethodHandle :: invokeExact作为方法引用导致的LambdaConversionException引起的BootstrapMethodError的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!