Java 使用Eclipse编译器为lambda生成的重复方法
这条线Java 使用Eclipse编译器为lambda生成的重复方法,java,eclipse,lambda,java-8,Java,Eclipse,Lambda,Java 8,这条线 ((UnaryOperator<Integer>)o->o).toString(); 就其本身而言,这并没有什么值得注意的,这是Eclipse Java 8编译器中的另一个bug。然而,我对失败的细节很感兴趣。如果我们启用jdk.internal.lambda.dumpProxyClasses系统属性并检索生成的lambda类代码,使用javap对其进行解析将显示该类定义了两个相同的apply方法,其中一个被标记为桥接方法: 我理解,为了保持向后兼容性,泛型需要桥接方
((UnaryOperator<Integer>)o->o).toString();
就其本身而言,这并没有什么值得注意的,这是Eclipse Java 8编译器中的另一个bug。然而,我对失败的细节很感兴趣。如果我们启用jdk.internal.lambda.dumpProxyClasses
系统属性并检索生成的lambda类代码,使用javap
对其进行解析将显示该类定义了两个相同的apply
方法,其中一个被标记为桥接方法:
我理解,为了保持向后兼容性,泛型需要桥接方法;然而,我无法理解Eclipse中的错误如何迫使JDK合成有缺陷的方法对
作为比较,如果我们稍微将Java行更改为:
((Object)((UnaryOperator<Integer>)o->o)).toString();
可能这实际上是JDK中的一个bug,但它不是由javac
引起的吗
我正在OSX上使用JavaC1.8.0\u20
,并使用带有Java8补丁的Eclipse开普勒SR2
更新:引导方法调用
Eclipse编译器负责发出正确的InvokedDynamic引导方法调用(lambda元工厂)。以下是失败案例的引导方法参数的外观:
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;
由于Brian的帮助,我现在清楚地看到上面最后两行造成了错误:
上的数字1表示“有一种桥接方法”李>#47
(Ljava/lang/Object;)Ljava/lang/Object
上的code>描述了桥接方法的签名,这显然与主签名相同#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;
这里使用了更简单的
元工厂
方法,并且没有创建桥接方法。根据您发布的堆栈跟踪,这几乎肯定是Eclipse代码生成中的一个bug,而不是JDK。您可以从捕获lambda(由ecj生成)的代码的javap
列表中找到这一点。我想您会发现,它调用了替代元工厂(altMetafactory
),它处理异常情况,例如可序列化lambda,由lambda对象实现的附加标记接口,或目标接口未处理的桥接方法
仅供参考,明确要求额外桥梁的情况如下:
这两种情况都很严重,应该很少发生。是的,我已经确定使用了
altMetaFactory
,并且只在失败的情况下使用。我仍然找不到提供方法实现的代码;我甚至不知道在哪里可以找到它。它在恒定池中吗?另外,我希望lambda工厂机制足够高,不允许创建无效类。基本上,我试图深入了解lambda(meta)工厂的内部工作原理。如果您将静态引导args(常量池中的引导方法表,javap-v)的javap列表发布到altMetaFactory引导,我可以告诉您它应该说什么。bootstrap方法列表就在javap-v-c
列表中的常量池之后;通过查看捕获所需lambda的invokedynamic
指令,在表中找到正确的条目。我已经粘贴了它,但我已经感谢您回答我的问题。显然,问题发生在标记为#48
的条目上。我已经与altMetaFactory
的签名进行了交叉检查,这是“bridges”参数。是的,eclipse要求一个冗余桥接器。这从未向eclipse报告过。现在尝试关闭循环,我找不到任何发布版本的ecj,它为本例创建了涉及altMetaFactory
的代码。听起来ecj和javac从Java8GA开始就在这里达成了一致(除非这个例子比上面显示的更多)。
{
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
}
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;
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;