I found a strange compilation restriction which I cannot explain, and I don't understand this restriction's reason.
Example-1:
Consider these classes:
In package e1;
:
public class C1 {
enum E1 { A, B, C }
public E1 x;
}
In package e2;
:
import e1.C1;
public class C2 {
public String test(C1 c1) {
return c1.x.toString(); // here compilation error
}
}
This causes the following compilation error:
Error:(5,20) java:
toString()
injava.lang.Enum
is defined in an inaccessible class or interface
Example-2:
Consider these classes:
In package i1;
:
public interface I1 {
int someMethod();
}
public class C1 {
static class I2 implements I1 {
public int someMethod() {
return 1;
}
}
public I2 x = new I2();
}
In package i2;
:
import i1.C1;
import i1.I1;
public class C2 {
public static void main(String[] args) {
C1 c1 = new C1();
System.out.println(c1.x.someMethod()); // compilation error
}
}
This also causes the same compilation error, but if we change the offending line to:
System.out.println(((I1)c1.x).someMethod());
Then this can be compiled and works fine.
So, the question is:
Why is this restriction of accessibility needed?
Yes, I understand that classes C1.E
in example-1) and C1.I2
in example-2) are package private. But at the same time it's clear that nobody could assign weaker access privileges to methods of a base interface (I1
of Object
), so it will be always safe to make direct casting of object to its base interface and get access to restricted method.
Could somebody explain the purposes and the reason of this restriction?
UPDATE: assylias pointed out JLS §6.6.1:
A member (class, interface, field, or method) of a reference (class, interface, or array) type or a constructor of a class type is accessible only if the type is accessible…
Looks like this is the restriction, but it doesn't explain why this restriction (in the cases of the above mentioned examples) is needed…
Best Answer
For invocation of instance methods invokevirtual instruction is used. To invoke this method class must have a resolved reference to this method
From invokevirtual specification:
5.4.3.3. Method Resolution:
5.4.3.1. Class and Interface Resolution:
5.4.4. Access Control:
C and D are not from the same package. So even if java compiles this code for you, it will throw an IllegalAccessError during invocation. Compiler is clever enough to prevent such obvious errors. These restrictions are coming from requirements of java's class resolution process.
To invoke instance method JVM requires two things: reference to the object and description of the object(class or interface). Description is accessed through resolution process. If it fails, invocation fails.
In your case C2 has access to I1. So interface invocation works well. But C2 does not have access to class I2. And that's why IllegalAccessError could be thrown at runtime if this code compiles.
How to reproduce IllegalAccessError: