I'm kind of new to visibility and want to really internilize these concepts.
I've learnt that "package-private", the default visibility in Java, allows us to see fields from classes in the same package; and that protected, apart from implying package visibility,
also allows us, from class Child
in otherPackage
, get fields declared in Parent
class in thisPackage
,
as long as we are asking for them to an instance of class Child
or a descendant.
However, this case really confuses me:
package thisPackage;
public class Parent {
protected int protectedField;
int packagePrivateField;
}
package otherPackage;
import thisPackage.Parent;
public class ChildOtherPackage extends Parent {
}
Under this situation, I would guess that this code wouldn't work (because we are asking an instance of ChildOtherPackage
for the protected
field, and ChildOtherPackage
does not inherit from GrandChild
), but it does:
package thisPackage;
import otherPackage.ChildOtherPackage;
public class GrandChild extends ChildOtherPackage {
public void test (ChildOtherPackage c) {
System.out.println(c.protectedField);
}
}
And also, this code that I would expect to work (because classes GrandChild
and Parent
are declared in the same
package), doesn't:
package thisPackage;
import otherPackage.ChildOtherPackage;
public class GrandChild extends ChildOtherPackage {
public void test (GrandChild g) {
System.out.println(g.packagePrivateField); // Error: packagePrivateField is not public in Parent; cannot be accessed from outside package
}
}
What's going on here? Maybe the behaviour in the second example has to do with the fact that inheritance really represent implicit composition internally and when we ask for g.packagePrivateField
, g
asks his internal ChildOtherPackage
object for the packagePrivateField
, and in this question we are soliciting a package-private field from outside the package thisPackage
; but I'm not really sure.
Any help is really appreciated.
TL;DR: I expected (under the environment of the first two code snippets) the first code not to work and the second one to work, and neither of those happened.
In the first case, accessing c.protectedField
is allowed because you are accessing it from the same package in which protectedField
is declared. You even said "apart from implying package visibility..." yourself.
From the Access Control section of the Java Language Specification,
Otherwise, if the member or constructor is declared protected, then access is permitted only when one of the following is true:
Access to the member or constructor occurs from within the package containing the class in which the protected member or constructor is declared.
Access is correct as described in §6.6.2.
The access is in the same package, so whatever §6.6.2 says doesn't even matter.
In the second case, accessing c.packagePrivateField
does not compile, because packagePrivateField
is not a member of GrandChild
.
The relevant section of the JLS is Class Members.
The members of a class are all of the following:
- Members inherited from its direct superclass type (§8.1.4), except in the class Object, which has no direct superclass type
- ...
Only members of a class that are declared
protected
orpublic
are inherited by subclasses declared in a package other than the one in which the class is declared.
From this, we can see that ChildOtherPackage
inherits the protectedField
, but does not inherit packagePrivateField
, because ChildOtherPackage
is declared in the package thatPackage
, not thisPackage
.
As a result, packagePrivateField
is not a member of GrandChild
either, because only members of the direct superclass is inherited.
This is ultimately because packagePrivateField
is package private, so presumably that is why the error message points out the accessibility issue, instead of something like "cannot resolve symbol..." like it normally does when you try to access a non-existent member.