Почему эта ссылка на метод не работает во время выполнения, а не на соответствующий лямбда-вызов?
У меня есть эти два интерфейса. Один является общедоступным (A), другой - частным (AA). A
расширяет AA
.
package pkg.a;
@FunctionalInterface
public interface A extends AA {
}
.
package pkg.a;
interface AA {
default void defaultM() {
System.out.println(m());
}
String m();
}
У меня есть этот код (в другой упаковке):
package pkg;
import java.util.Arrays;
import java.util.List;
import pkg.a.A;
public class Test {
public static void main(String[] args) {
List<A> list = Arrays.asList(() -> "imp1", () -> "imp2");
list.stream().forEach(a -> a.defaultM());
list.stream().forEach(A::defaultM);
}
}
При запуске приведенного выше кода list.stream().forEach(A::defaultM);
выдает приведенное ниже исключение. Почему? Почему ссылка на метод не может получить доступ к методам, определенным в частном интерфейсе пакета, а лямбда-выражение может? Я запускаю это в Eclipse (версия: 2018-12 (4.10.0)) с версией Java 1.8.0_191.
imp1
imp2
Exception in thread "main" java.lang.BootstrapMethodError: call site initialization exception
at java.lang.invoke.CallSite.makeSite(CallSite.java:341)
at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:307)
at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:297)
at pkg.Test.main(Test.java:14)
Caused by: java.lang.IllegalArgumentException: java.lang.IllegalAccessException: class is not public: pkg.a.AA.defaultM()void/invokeInterface, from pkg.Test
at java.lang.invoke.MethodHandles$Lookup.revealDirect(MethodHandles.java:1360)
at java.lang.invoke.AbstractValidatingLambdaMetafactory.<init>(AbstractValidatingLambdaMetafactory.java:131)
at java.lang.invoke.InnerClassLambdaMetafactory.<init>(InnerClassLambdaMetafactory.java:155)
at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:299)
at java.lang.invoke.CallSite.makeSite(CallSite.java:302)
... 3 more
Caused by: java.lang.IllegalAccessException: class is not public: pkg.a.AA.defaultM()void/invokeInterface, from pkg.Test
at java.lang.invoke.MemberName.makeAccessException(MemberName.java:850)
at java.lang.invoke.MethodHandles$Lookup.checkAccess(MethodHandles.java:1536)
at java.lang.invoke.MethodHandles$Lookup.revealDirect(MethodHandles.java:1357)
... 7 more
Ответы
Ответ 1
Это ошибка:
Ссылка на метод использует неверный квалифицирующий тип.
Ссылка на метод, объявленный в классе доступа к пакету (через public subtype) компилируется в лямбда-мост; квалифицирующий тип в Метод моста является декларирующим классом, а не ссылочным классом. Это приводит к IllegalAccessError.
Исправлено в Java 9.
Ответ 2
Это кажется ошибкой в некоторых версиях Java.
Я могу скопировать его, если скомпилирую и запусту с JDK 8, а именно:
tj$ javac -version
javac 1.8.0_74
tj$ java -version
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)
... но не с JDK 11 или 12, а именно:
tj$ javac -version
javac 11.0.1
tj$ java -version
openjdk version "11.0.1" 2018-10-16
OpenJDK Runtime Environment 18.9 (build 11.0.1+13)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.1+13, mixed mode)
и
tj$ javac -version
javac 12.0.2
tj$ java -version
java version "12.0.2" 2019-07-16
Java(TM) SE Runtime Environment (build 12.0.2+10)
Java HotSpot(TM) 64-Bit Server VM (build 12.0.2+10, mixed mode, sharing)
Я также могу реплицировать его, если я скомпилирую с JDK 8, но запустю его с JDK 12, предлагая проблему компиляции.