JDK是Oracle的JDK 1.8u65,但“低至”1.8u25也存在这个问题。
下面是完整的SSCCE:
public final class Foo
{
private interface X
{
default void x()
{
}
}
private enum E1
implements X
{
INSTANCE,
;
}
private enum E2
implements X
{
INSTANCE,
;
}
public static void main(final String... args)
{
Stream.of(E1.INSTANCE, E2.INSTANCE).forEach(X::x);
}
}
这段代码可以编译,但在运行时失败:
Exception in thread "main" java.lang.BootstrapMethodError: call site initialization exception
at java.lang.invoke.CallSite.makeSite(CallSite.java:341)
at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:307)
at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:297)
at com.github.fge.grappa.debugger.main.Foo.main(Foo.java:38)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.lang.invoke.LambdaConversionException: Invalid receiver type class java.lang.Enum; not a subtype of implementation type interface com.github.fge.grappa.debugger.main.Foo$X
at java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:233)
at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:303)
at java.lang.invoke.CallSite.makeSite(CallSite.java:302)
... 8 more
在代码中修复它是“容易的”;在main方法中,您只需:
// Note the <X>
Stream.<X>of(E1.INSTANCE, E2.INSTANCE).forEach(X::x);
EDIT实际上还有第二种方法,就像公认的答案中提到的那样……将方法引用替换为lambda:
Stream.of(E1.INSTANCE, E2.INSTANCE).forEach(x -> x.x());
所以,嗯。这里发生了什么?为什么要首先编译初始代码?我本以为编译器会注意到方法引用不是在任何Enum<?>
上,而是在X
上,但没有...
我遗漏了什么?这是编译器中的错误吗?对我的误解?
发布于 2015-11-26 04:49:09
看起来你遇到了JDK-8141508,在处理交叉点类型和方法引用时,这确实是javac
的一个缺陷。计划在Java 9中修复该问题。
假设我们有这样的代码,
公共类交集{接口I {}接口J{ void foo();}静态 void bar(T t) { Runnable r= t::foo;}公共静态void main(String[]参数){类A实现I,J{ public void foo() {}} bar(new A());}}
目前,javac在J::foo上生成一个方法引用,并使用一个带I作为参数的invokedynamic,因此它在运行时失败。javac应该把t::foo去糖成一个lambda,这个lambda接受一个I,然后添加一个强制转换到J,就像调用一个交叉点类型的方法一样。
因此,解决方法是使用lambda,
Runnable r=t -> t.foo();
我已经在某个地方看到了这个错误,但在数据库中找不到相应的错误报告:(
在您的代码中,Stream.of(E1.INSTANCE, E2.INSTANCE)
创建的流的类型是Stream<Enum<?>&Foo.X>
,它结合了bug的所有元素:交集类型和方法引用。
正如Remi Forax所指出的,一种变通方法是:
Stream.of(E1.INSTANCE, E2.INSTANCE).forEach(x -> x.x());
即使用显式lambda表达式而不是方法引用。
https://stackoverflow.com/questions/33925551
复制相似问题