В одном пакете (a
) у меня два функциональных интерфейса:
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);
}
}
Первая реализация использует анонимный класс и работает без проблем. Второй, с другой стороны, компилируется нормально, но во время выполнения происходит сбой, вызывающий 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
Я думаю, было бы разумнее, если бы лямбда-выражение работало так же, как анонимный класс, или выдавало бы ошибку времени компиляции. Так что мне просто интересно, что здесь происходит.
Я попытался удалить суперинтерфейс и объявить метод в SomeApplicable
следующим образом:
package a;
@FunctionalInterface
public interface SomeApplicable {
void apply(SomeApplicable self);
}
Это, очевидно, заставляет его работать, но позволяет нам увидеть, что отличается в байт-коде.
Синтетический метод 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
.
Я действительно не знаю, как работает лямбда-метафабрика, но я думаю, что это может быть ключевым отличием.
Я также попытался явно объявить метод 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
Caused by: java.lang.IllegalAccessError: tried to access class a.Applicable from class b.Test
в строке лямбда-выражения. - person Bubletan   schedule 26.10.2016The type Applicable<SomeApplicable> from the descriptor computed for the target context is not visible here.
- person dcsohl   schedule 26.10.2016a
и один в пакетеb
. - person Bubletan   schedule 26.10.2016javac
, не с Eclipse, возможно, ошибка. - person Sotirios Delimanolis   schedule 26.10.2016