This is a compiler bug, related reports are
- JDK-8152643: “Javac compiles method reference that allows results in an IllegalAccessError”
- JDK-8059632: “Method reference compilation uses incorrect qualifying type”
Note that the first report has the status “Fixed in 8u102”, so downloading JDK8u102 could solve the issue. Of course, when using a compiler other than javac
, e.g. ECJ, you have to ensure that that compiler is up to date as well.
In either case, you have to recompile the source code, as it is a compiler issue. But then, the compiled code should even work with older JREs.
To explain the issue, normally, invocations should be encoded into the byte code using the compile-time type of the receiver (or the explicit type in case of static
methods), regardless of the declaring type of the actual method implementation. So if you have a public
class A
inheriting the public
method foo
from the non-public
class B
, an invocation of A.foo
should be encoded as A.foo
rather than B.foo
. For ordinary invocations, the compilers work that way, but for method references, javac
(and afaik also older versions of ECJ) failed to do that correctly. So when encountering a class trying to access B.foo
directly without having access to B
, an IllegalAccessError
is thrown.
It works when using a lambda expression instead, as then, the invocation is compiled into an ordinary invocation instruction, for which the compiler works correctly, within a synthetic method and a reference to that synthetic method is used when constructing an instance of the functional interface at runtime. Since the synthetic method recides within the same class, it’s always accessible.