gpt4 book ai didi

java - 使用 Eclipse 编译器为 lambda 生成的重复方法

转载 作者:塔克拉玛干 更新时间:2023-11-01 22:45:35 25 4
gpt4 key购买 nike

这一行

((UnaryOperator<Integer>)o->o).toString();

在类中的任何地方编写,并使用 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

就其本身而言,这并不是什么值得注意的问题,这是 Eclipse 的 Java 8 编译器中的另一个错误。但是,我对失败的细节很感兴趣。如果我们启用 jdk.internal.lambda.dumpProxyClasses 系统属性并检索生成的 lambda 类代码,使用 javap 解析它会发现该类有两个相同的 定义了 apply 方法,其中一个标记为 bridge 方法:

{
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
}

我理解泛型需要桥接方法以保持向后兼容性;但是,我无法理解 Eclipse 中的错误如何迫使 JDK 合成有缺陷的方法对。

为了比较,如果我们将 Java 行稍微更改为:

((Object)((UnaryOperator<Integer>)o->o)).toString();

然后我们只得到一个非桥接方法,并且没有失败:

{
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
}

难道这实际上是 JDK 中的一个错误,但它不是 javac 引起的?

我在 OS X 和带有 Java 8 补丁的 Eclipse Kepler SR2 上使用 javac 1.8.0_20

更新:引导方法调用

Eclipse 编译器负责发出正确的 invokedynamic Bootstrap 方法调用(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 的帮助,我现在很清楚上面的最后两行造成了错误:

  • #47 上的数字 1 表示“只有一种桥接方法”;
  • (Ljava/lang/Object;)Ljava/lang/Object;#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;

这里使用了更简单的 metafactory 方法,没有创建桥接方法。

最佳答案

根据您发布的堆栈跟踪,这几乎可以肯定是 Eclipse 代码生成中的错误,而不是 JDK。您可以从捕获 lambda(由 ecj 生成)的代码的 javap 列表中找到这一点。我认为您会发现它调用备用元工厂(altMetafactory) 处理异常情况,例如可序列化的 lambda、由 lambda 对象实现的附加标记接口(interface),或目标接口(interface)未处理的桥接方法。

作为引用,唯一明确需要额外桥接的情况是

  1. 当目标接口(interface)需要桥接但使用较旧的 javac 编译,因此接口(interface)本身不存在桥接时,或者
  2. 当目标类型和其他接口(interface)之间的交互需要桥时。

两者都是严重的极端情况,应该极少发生。

关于java - 使用 Eclipse 编译器为 lambda 生成的重复方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25708873/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com