Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do child classes cast to parent type default to parent version of private instance methods, but not of other instance methods?

It was my understanding that a child cast to the parent type (as in Super sc = new Child();) would call the parent class's static methods, and access the parent class's non-hidden fields, but would make use of the child class's instance methods.

This does not seem to hold true for the case of private instance methods (those in which overriding does not actually take place because the parent method is private.)

I understand that the fact that there is no overriding (private parent methods are not visible to the child, so they can only be hidden.) would mean that the child wouldn't look up the heirarchy to see the parent's version of the method when the instance method is invoked from the child.
However, the exact opposite of what I'd expect (the version present in the child being called) is occurring. The parent version of the private method is invoked.

Why does this happen if the instance methods of the child are typically invoked, and within the child, there is a method with an identical signature (a perfect match for the invocation of method2() in this case) already present, hiding the same method in the parent class?

package whatever;

public class A {

    public void method1(){
        System.out.println("A method1().");
    }
    //using "final" here to emphasize that this is a hiding, not an override. 
    private final void method2(){  
        System.out.println("A private method2().");
    }

    public static void main(String[] args)
    {
        A a = new A().new B();
        a.method1(); //calls B  method 1
        ((A.B)a).method1(); //calls B method 1 
        a.method2(); //calls A private method 2 **I expected it to call B private method 2
        ((A.B)a).method2(); //calls B private method 2

    }

    public class B extends A {

        public void method1(){
            System.out.println("B method1().");
        }
        private final void method2(){
            System.out.println("B private method2().");
        }
    }

}
like image 718
Mer Avatar asked Oct 31 '22 17:10

Mer


1 Answers

First, B is actually a nested class and, oddly enough, the private members of nested classes can by accessed by the outer class. Otherwise, it wouldn't even be legal to do ((A.B)a).method2(); (The reverse is also true. B can access private members in A as well.)

That said, to answer your actual question, the reason a.method2(); calls the private method in A is that private methods are not virtual, as they cannot be overriden at all. (How could they be, if inheriting classes aren't supposed to even know about them?) As such, they are bound at compile time, which means using the declared type of the variable. A a is declared as A, but (A.B)a is cast at compile time to B. Thus, your first call uses the private method from A and your second call uses the private method from B.

It's quirky that the facts in those two paragraphs don't interact with each other. It might reasonable to expect that, since the nested class "knows" about the outer class's private members, an override might be possible. But it's not. Private functions never show up in the vTable and can never be overridden.

like image 89
João Mendes Avatar answered Nov 08 '22 11:11

João Mendes