Lambda 表达式在运行时失败,并带有 java.lang.BootstrapMethodError
在一个包()中,我有两个功能接口:a
package a;
@FunctionalInterface
interface Applicable<A extends Applicable<A>> {
void apply(A self);
}
-
package a;
@FunctionalInterface
public interface SomeApplicable extends Applicable<SomeApplicable> {
}
超接口中的方法采用 a,因为否则,如果改用,则类型在包外部不可见,因此无法实现该方法。apply
self
A
Applicable<A>
在另一个包()中,我有以下类:b
Test
package b;
import a.SomeApplicable;
public class Test {
public static void main(String[] args) {
// implement using an anonymous class
SomeApplicable a = new SomeApplicable() {
@Override
public void apply(SomeApplicable self) {
System.out.println("a");
}
};
a.apply(a);
// implement using a lambda expression
SomeApplicable b = (SomeApplicable self) -> System.out.println("b");
b.apply(b);
}
}
第一个实现使用匿名类,它可以毫无问题地工作。另一方面,第二个编译良好,但在运行时失败,在尝试访问接口时由 由 导致。java.lang.BootstrapMethodError
java.lang.IllegalAccessError
Applicable
Exception in thread "main" java.lang.BootstrapMethodError: java.lang.IllegalAccessError: tried to access class a.Applicable from class b.Test
at b.Test.main(Test.java:19)
Caused by: java.lang.IllegalAccessError: tried to access class a.Applicable from class b.Test
... 1 more
我认为如果 lambda 表达式像匿名类一样工作或给出编译时错误,那会更有意义。所以,我只是想知道这里发生了什么。
我尝试删除超接口并声明其中的方法,如下所示:SomeApplicable
package a;
@FunctionalInterface
public interface SomeApplicable {
void apply(SomeApplicable self);
}
这显然使它工作,但允许我们看到字节码中有什么不同。
从 lambda 表达式编译的合成方法在这两种情况下似乎完全相同,但我可以在 bootstrap 方法下的方法参数中发现一个差异。lambda$0
Bootstrap methods:
0 : # 58 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:
#59 (La/Applicable;)V
#62 invokestatic b/Test.lambda$0:(La/SomeApplicable;)V
#63 (La/SomeApplicable;)V
从 更改为 。#59
(La/Applicable;)V
(La/SomeApplicable;)V
我真的不知道lambda metafactory是如何工作的,但我认为这可能是一个关键的区别。
我还尝试像这样显式声明该方法:apply
SomeApplicable
package a;
@FunctionalInterface
public interface SomeApplicable extends Applicable<SomeApplicable> {
@Override
void apply(SomeApplicable self);
}
现在该方法实际存在,编译器为 生成一个桥接方法。在运行时仍会引发相同的错误。apply(SomeApplicable)
apply(Applicable)
在字节码级别,它现在使用而不是:LambdaMetafactory.altMetafactory
LambdaMetafactory.metafactory
Bootstrap methods:
0 : # 57 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:
#58 (La/SomeApplicable;)V
#61 invokestatic b/Test.lambda$0:(La/SomeApplicable;)V
#62 (La/SomeApplicable;)V
#63 4
#64 1
#66 (La/Applicable;)V