CodeMonkey CodeMonkey - 1 month ago 7
Java Question

Java access to protected member in subclass in different package, using object reference of parent type

I have the following code in two separate files.

package animal;

public class Frog
{
protected void ribbit()
{
System.out.println("In Frog class!");
}
}





package other;

import animal.*;

public class Tadpole extends Frog

{
protected void ribbit()
{
System.out.println("In Tadpole class!");
}

public static void main(String[] args)
{
Tadpole t = new Tadpole();
t.ribbit();

Frog f = new Tadpole();
f.ribbit(); // Does not compile
}
}


The first
Tadpole
object assigned to
Tadpole
type obviously compiles fine and the call to
ribbit()
will be to the
Tadpole
's
ribbit()
implementation. The second
Tadpole
object that is created and assigned to a
Frog
reference. However, the call to
ribbit()
results in a compiler error.

I know that if you create a subclass object in the subclass and assign to a superclass reference that is outside of the subclass's package and try to call a superclass method, this is not allowed. But in this case, shouldn't polymorphism make the object reference "f" call the
Tadpole
's
ribbit()
method since a
Tadpole
object was assigned to it? Why does this cause a compiler error and why is this is not allowed?

Answer

This is related to the rules about access to protected class members. See this section from the Java Language Specification on details, specifically:

Let C be the class in which a protected member is declared. Access is permitted only within the body of a subclass S of C.

In addition, if Id denotes an instance field or instance method, then:

  • If the access is by a qualified name Q.Id or a method reference expression Q :: Id (§15.13), where Q is an ExpressionName, then the access is permitted if and only if the type of the expression Q is S or a subclass of S.

  • If the access is by a field access expression E.Id, or a method invocation expression E.Id(...), or a method reference expression E :: Id, where E is a Primary expression (§15.8), then the access is permitted if and only if the type of E is S or a subclass of S.

So within the body of a subclass of Frog, you can only access x.ribbit() if x is a subclass of Frog (x cannot be declared a Frog).

This restriction exists on protected members, because otherwise, imagine that Frog has a protected int field:

public class Frog {
    protected int a = 1;

    ...
}

Then one would be able to define a public method in the subclass of Frog:

public class TadPole extends Frog {

    public int revealFieldValueOfParent(Frog frog) {
        return frog.a;  // imagine this was OK
    }
}

Then any other (unrelated) class would be able to access the field by passing a Frog to the method of the subclass:

public class SomeOtherClass {

    public static void main(String[] args) {
         TadPole tadpole = new TadPole();
         Frog frog = new Frog();
         int revealedValue = tadpole.revealFieldValueOfParent(frog);
         // print revealedValue
    }
}

EDIT:

This compiler error has nothing to do with polymorphism. Polymorphism, as related to the actual type of the object, is a runtime aspect and the compiler does not try to think whether at runtime the variable f actually refers to a Frog or to a Tadpole. All the compiler is doing here is enforcing the rules of the protected modifier, nothing more.

EDIT 2:

Based on the comments below, the revealFieldValueOfParent(Frog frog) method would actually reveal the value of the protected if we change it to revealFieldValue(TadPole frog), but you can also do that revealing trick with private members (i.e. similar to a getter method). It would really become the responsibility of the subclass to know what it's doing.