How does protected access work in Java, and what does it let a subclass do that package-private access does not?
Protected access combines package access with one extra privilege. A protected member is visible to every class in the same package, exactly like a package-private member, and additionally to any subclass, even one declared in a different package. That last part is the difference, and it is the reason to be careful.
public class Main {
public static void main(String[] args) {
Base b = new Base();
System.out.println(b.describe());
Child c = new Child();
c.useProtected();
System.out.println("after child = " + b.expose());
}
}
class Base {
protected int counter = 0;
protected void bump() { counter = counter + 1; }
public String describe() { bump(); return "counter=" + counter; }
public int expose() { return counter; }
}
class Child extends Base {
void useProtected() { bump(); }
}The example shows the access in action and also shows the danger. The child calls the inherited protected method, which changes the inherited protected field. Because the field is one value, not one per subclass, the child has just modified the base object's state. The output shows the counter going to one through the base's own method and then to two after the child touches it.
That is the practical problem with protected mutable state. The base class can no longer guarantee anything about itself, because any subclass, including ones written by a different team, can write to its fields. The base class has handed away ownership of its own invariants.
The safer arrangements are worth naming. Make the member private and give the subclass a protected method that performs the change correctly, so the rule stays where it can be enforced. Or make it final, so the subclass can read it but not write it. Or keep it package-private and use composition. Each of these gives the subclass what it needs without giving it the ability to break the parent's contract.