how to get the type of variable referenced in Java?

advertisements

I have the following code. Two questions follows:

class Parent {  

        private void test()
        {
            System.out.print("test executed!");
        }
        static void print(){
            System.out.println("my class is parent");
        }
}

class Child extends Parent
    {
    static void print(){
        System.out.println("my class is Child");
    }

    }  

   public class Inheritence {

        public static void main(String[] args)
        {
            Child p1 = new Child();
            Parent p = new Child();
            System.out.println("The class name of p is "+p.getClass());
            System.out.println("The class name of p1 is "+p1.getClass());
            System.out.println("p instance of child "+ (p instanceof Child));
            System.out.println("p1 instance of child "+ (p1 instanceof Child));
            //p.test();
            p.print();
        }
    }

The output is:

The class name of p is class Child
The class name of p1 is class Child
p instance of child true
p1 instance of child true
my class is parent

I thought the classname for p will be Parent, since it is of type Parent. However, it prints as Child. so how would I get the type of p.

Second question here is whether private methods are inherited or not. while many articles including this, comments that private methods are not inherited, I see in the below example it is inherited. It could be some type casting issues below.

class Child1 extends Parent1
    {  

    }  

    public class Parent1 {  

        private void test()
        {
            System.out.print("test executed!");
        }  

        public static void main(String[] args)
        {
                Parent1 p = new Child1();
                p.test();
                Child1 c = new Child1();
                //c.test(); The method test from parent1 is not visible
            }
        }  

Output is : test executed!

here I am calling test method on Child1 object which is of type Parent1. Child1 has no test method since it is not inherited. but I still get the output, which indicates the private methods are inherited! If test was a protected method, and I override in child class, it is the overridden method that gets executed though the type of object on which it is called is parent(parent p1 = new child1());

EDIT: After few comments, I made class Parent1 and class Child1 seperate and a new Class called App that constructs a parent and child object. now I cannot call p.test in the code below.

class Child1 extends Parent1
    {  

    }  

     class Parent1 {  

        private void test()
        {
            System.out.print("test executed!");
        } }

    public class App1{

        public static void main(String[] args)
        {
            Parent1 p = new Child1();
            p.test();//The method test from parent is not visible
            Child1 c = new Child1();
            //c.test(); //The method test from parent1 is not visible

        }
    }


To address the second question: it may be helpful to keep the concepts of "inheritance" and "visibility" distinct. A private method m is only visible inside the class C where the method is declared. So you can't use m at all from outside C. Furthermore, even inside C, you can't use x.m() unless the object x is declared to be of type C. But objects of a subclass still have the method m, and that method can be called, but only inside C. This example will compile (even if the two classes are in separate files):

public class Class1 {
    private void test () { System.out.println ("test"); }
    public void doThis (Class2 c) {
        // c.test();  -- does not compile [1]
        ((Class1)c).test();
    }
}

public class Class2 extends Class1 {
    public void doSomething () {
        doThis (this);
        // ((Class1)this).test();  -- does not compile [2]
    }
}

Note that inside doThis, you can still call the test method of c, even though c has type Class2. However, you can do this only because the code is inside the class Class1 that declares the test() method (which is why [2] doesn't compile); and you can do it only by casting c to an object of type Class1 (which is why [1] doesn't compile). However, even after you cast it so that the compiler sees it as an expression of type Class1, the actual object still has type Class2. And if you were to call an overridden method polymorphic like this: ((Class1)c).polymorphic(), it would call the method defined in Class2, not Class1, since the object is still actually a Class2 object.

So I think that in some sense, test is inherited even though it's private; it just isn't visible in Class2.

MORE: I think it's also helpful to understand that there's a difference between a compile-time type and a run-time type (or instance type). If you declare a variable Parent x; then x's compile-time type is Parent. If you have a function f whose return type is Parent, then an expression like obj.f(arg,arg2) will have type Parent. But at run-time, if a variable or expression has compile-time type Parent, the actual type at run-time can be Parent or any of its subclasses. The run-time type will be based on how the object was constructed. So a variable can have a compile-time type Parent and a run-time type Child. Then you just need to know which type is used and when. For checking whether a method is visible (which is where private comes into play), the compile-time type is used. For deciding which method to call when a child subclass overrides a method, the run-time type is used (that's what polymorphism is). For .getClass(), the run-time type is used. Anyway, that's how I think about things so that I don't get too confused.

EXAMPLE: Let's say we have:

class Parent { }
class Child extends Parent { }
class Grandchild extens Child { }

In some other class, we have

Parent x1 = new Parent();
Parent x2 = new Child();
Parent x3 = new Grandchild();

The variables x1, x2, and x3 all have a compile-time type of Parent. This means all three variables could refer to an instance of Parent, or an instance of Child or Grandchild or any other subclass of Parent. And that's what happens above: x2 will refer to an instance of Child, and x3 will refer to an instance of Grandchild.

Similarly:

private Parent getAParent(int n) {
    if (n == 0) return new Parent();
    if (n == 1) return new Child();
    if (n == 2) return new Grandchild();
    throw new IllegalArgumentException();
}

Parent x4 = getAParent (0);
Parent x5 = getAParent (1);
Parent x6 = getAParent (2);

x5 refers to an instance of Child, and x6 refers to an instance of Grandchild.

But the compile-time type of all the variables and the getAParent calls is still Parent. The compiler does not know what class the variable or function call actually refers to when you run the program. So if you declare a method play() inside Grandchild, these are still illegal:

x3.play ();             // ERROR
x6.play ();             // ERROR
getAParent(2).play ();  // ERROR

because the compiler thinks of the two variables and getAParent(2) as having type Parent, not Grandchild. And play isn't defined for Parent. To see if those variables have a run-time type that has a play method would require the compiler to generate code to check at run-time, and the compiler doesn't do that.

That's why p.test() works in your second example. Even though p refers to an instance of Child1, p's compile-time type is Parent1 since you declared it as having type Parent1. And the compiler sees that Parent1 has a test method; and since the code is inside Parent1, the test method is visible even though it's private. That's why it works. The compiler doesn't generate code to check what run-time type p actually refers to. Hope this helps explain things.