Mar*_*nik 3 java eclipse lambda java-8
这条线
((UnaryOperator<Integer>)o->o).toString();
Run Code Online (Sandbox Code Playgroud)
写在类中的任何地方,并使用Eclipse Kepler编译,在执行时到达该行时将导致失败:
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 test.Test.main(Test.java:7)
Caused by: java.lang.ClassFormatError: Duplicate method name&signature in class file test/Test$$Lambda$1
at sun.misc.Unsafe.defineAnonymousClass(Native Method)
at java.lang.invoke.InnerClassLambdaMetafactory.spinInnerClass(InnerClassLambdaMetafactory.java:324)
at java.lang.invoke.InnerClassLambdaMetafactory.buildCallSite(InnerClassLambdaMetafactory.java:194)
at java.lang.invoke.LambdaMetafactory.altMetafactory(LambdaMetafactory.java:474)
at java.lang.invoke.CallSite.makeSite(CallSite.java:301)
... 2 more
Run Code Online (Sandbox Code Playgroud)
就其本身而言,这不是什么值得注意的,而是Eclipse Java 8编译器中的另一个错误.但是,我对失败的细节很感兴趣.如果我们启用jdk.internal.lambda.dumpProxyClasses系统属性并检索生成的lambda类代码,解析它将javap显示该类有两个相同的apply方法定义,其中一个标记为桥接方法:
{
public java.lang.Object apply(java.lang.Object);
descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
flags: ACC_PUBLIC
Code:
stack=1, locals=2, args_size=2
0: aload_1
1: checkcast #14 // class java/lang/Integer
4: invokestatic #20 // Method test/Test.lambda$0:(Ljava/lang/Integer;)Ljava/lang/Integer;
7: areturn
public java.lang.Object apply(java.lang.Object);
descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
flags: ACC_PUBLIC, ACC_BRIDGE
Code:
stack=1, locals=2, args_size=2
0: aload_1
1: checkcast #14 // class java/lang/Integer
4: invokestatic #20 // Method test/Test.lambda$0:(Ljava/lang/Integer;)Ljava/lang/Integer;
7: areturn
}
Run Code Online (Sandbox Code Playgroud)
我知道泛型需要桥接方法以保持向后兼容性; 但是,我无法理解Eclipse中的错误如何迫使JDK合成有缺陷的方法对.
为了比较,如果我们稍微改变我们的Java行:
((Object)((UnaryOperator<Integer>)o->o)).toString();
Run Code Online (Sandbox Code Playgroud)
然后我们得到一个非桥接方法,没有失败:
{
public java.lang.Object apply(java.lang.Object);
descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
flags: ACC_PUBLIC
Code:
stack=1, locals=2, args_size=2
0: aload_1
1: checkcast #14 // class java/lang/Integer
4: invokestatic #20 // Method test/Test.lambda$0:(Ljava/lang/Integer;)Ljava/lang/Integer;
7: areturn
}
Run Code Online (Sandbox Code Playgroud)
难道这可能是JDK中的一个错误,但它不是由它引起的javac?
我javac 1.8.0_20在OS X和Eclipse Kepler SR2上使用Java 8补丁.
Eclipse编译器负责发出正确的invokedynamic bootstrap方法调用(lambda metafactory).这是失败案例的bootstrap方法参数的样子:
BootstrapMethods:
0: #39 invokestatic java/lang/invoke/LambdaMetafactory.altMetafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#41 (Ljava/lang/Object;)Ljava/lang/Object;
#44 invokestatic test/Test.lambda$0:(Ljava/lang/Integer;)Ljava/lang/Integer;
#45 (Ljava/lang/Integer;)Ljava/lang/Integer;
#46 4
#47 1
#48 (Ljava/lang/Object;)Ljava/lang/Object;
Run Code Online (Sandbox Code Playgroud)
由于Brian的帮助,现在我很清楚上面的最后两行会造成错误:
#47表示"有一种桥接方法";(Ljava/lang/Object;)Ljava/lang/Object;on #48描述了桥接方法的签名,这显然与主签名相同.为了比较,这是工作案例:
BootstrapMethods:
0: #53 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#55 (Ljava/lang/Object;)Ljava/lang/Object;
#58 invokestatic test/Test.lambda$0:(Ljava/lang/Integer;)Ljava/lang/Integer;
#59 (Ljava/lang/Integer;)Ljava/lang/Integer;
Run Code Online (Sandbox Code Playgroud)
这里使用了更简单的metafactory方法,并且没有创建桥接方法.
基于您发布的堆栈跟踪,这几乎肯定是Eclipse代码生成中的错误,而不是JDK.你可以从捕获 lambda(由ecj生成)javap的代码列表中找到它.我认为你会发现它会调用备用metafactory()来处理异常情况,例如可序列化lambda,附加标记接口由lambda对象实现,或者由目标接口未处理的桥接方法. altMetafactory
作为参考,唯一明确需要额外桥接的情况是
两者都是严重的角落案件,应该极少发生.
| 归档时间: |
|
| 查看次数: |
777 次 |
| 最近记录: |