FULL PRODUCT VERSION :
openjdk version "1.8.0-jdk8u76-b00"
OpenJDK Runtime Environment (build 1.8.0-jdk8u76-b00-20151028)
OpenJDK 64-Bit Server VM (build 25.66-b20151028, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Linux (hostname) 3.13.0-24-generic #46-Ubuntu SMP Thu Apr 10 19:11:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
A DESCRIPTION OF THE PROBLEM :
I have a package-private class "A" and a public class "B" that extends it. Class A has a public static method "print" that I want to access via a method pointer from class "C", which is in a different package. I use "B::print" as the method pointer, which compiles fine, but I get an IllegalAccessException when I run the code. It looks like the compiler is generating a class file that references A.print() instead of B.print(). The Eclipse compiler (where I originally filed this bug) generates a class file that correctly references B.print(), but the runtime error still occurs.
Jesper Moller provided some good information in the Eclipse bug I filed:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=480930#c4
I was able to work around the issue by making the base class public, but I'd rather leave it package-private, if possible.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Enter the attached code and try to execute the example.b.C class.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The generated class file should contain a reference to the example.a.B.print(int) method. The application should print out a list of integers between 0 and 9.
ACTUAL -
The generated class file contains a reference to the example.a.A.print(int) method. Execution fails with the attached error message.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.IllegalAccessError: tried to access class example.a.A from class example.b.C
at example.b.C.lambda$main$0(C.java:11)
at java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:110)
at java.util.stream.IntPipeline$Head.forEach(IntPipeline.java:557)
at example.b.C.main(C.java:11)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
// example/a/B.java
package example.a;
abstract class A
{
public static void print(int value)
{
System.out.println(value);
}
}
public class B extends A
{
// No additional functionality.
}
// example/b/C.java
package example.b;
import java.util.stream.IntStream;
import example.a.B;
public class C
{
public static void main(String[] args)
{
IntStream.range(0, 10).forEach(B::print);
// This line executes fine.
// IntStream.range(0, 10).forEach(value -> {B.print(value);});
}
}
// Jesper Moller provided this alternate version of C.java in the Eclipse bug I filed:
package example.b;
import java.lang.invoke.*;
import example.c.B;
public class C {
public static void main(String[] args) throws Throwable {
MethodHandles.Lookup me = MethodHandles.lookup();
MethodType t = MethodType.methodType(void.class);
MethodType rt = MethodType.methodType(Runnable.class);
MethodHandle b_print = me.findStatic(B.class, "print", t); // becomes A::print
CallSite site = LambdaMetafactory.metafactory(me, "run", rt, t, b_print, t);
MethodHandle factory = site.getTarget();
Runnable b_print_asRunnable = (Runnable) factory.invoke();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
I reluctantly made the base class public to work around this issue. I'd rather not expose the base class, though.