1 つのパッケージ ( a
) には、2 つの機能インターフェイスがあります。
package a;
@FunctionalInterface
interface Applicable<A extends Applicable<A>> {
void apply(A self);
}
-
package a;
@FunctionalInterface
public interface SomeApplicable extends Applicable<SomeApplicable> {
}
スーパーインターフェイスの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);
}
}
最初の実装は匿名クラスを使用しており、問題なく動作します。一方、2 番目のものは正常にコンパイルされますが、インターフェイスにアクセスしようとすると、 ajava.lang.BootstrapMethodError
が原因で実行時に a をスローして失敗します。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
ラムダ式が匿名クラスと同じように機能するか、コンパイル時にエラーが発生するかのどちらかであれば、より理にかなっていると思います。だから、私はここで何が起こっているのだろうと思っています。
スーパーインターフェースを削除して、次のようにメソッドを宣言しようとしましたSomeApplicable
:
package a;
@FunctionalInterface
public interface SomeApplicable {
void apply(SomeApplicable self);
}
これは明らかに機能しますが、バイトコードの違いを確認できます。
ラムダ式からコンパイルされた合成lambda$0
メソッドはどちらの場合も同じように見えますが、ブートストラップ メソッドの下のメソッド引数に 1 つの違いを見つけることができました。
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
ラムダメタファクトリーがどのように機能するかはよくわかりませんが、これが重要な違いかもしれません。
また、次のように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