javaobjectinstantiation

Object instantiation using different Constructors


Recently my teacher was talking about using different constructors to instantiate objects. But I'm really confused about that. For that I wanna understand why the I get the following compile error.

class SuperClass {
    void superClass(){
        System.out.println("superClass");
    }
}

class SubClass extends SuperClass {
    void subClass(){
        System.out.println("subClass");
    }
}

class Call {
    public static void main(String args[]){
        SuperClass s = new SubClass();
        s.superClass();
    }
}

When I compile and run the following code, I get the output

superClass

But when I try to call subClass() via the s Object, I get the following error.

damn.java:17: cannot find symbol
symbol  : method subClass()
location: class SuperClass
                s.subClass();
                 ^
1 error

OK so, according to this, I can assume that the even I instantiate object with a different constructor, only the Object type specified is loaded to the RAM.

But, when I use the override here like this,

class SuperClass {
    void superClass(){
        System.out.println("superClass");
    }
}

class SubClass extends SuperClass {
    void superClass(){
        System.out.println("subClass");
    }
}

class Call {
    public static void main(String args[]){
        SuperClass s = new SubClass();
        s.superClass();
    }
}

I get the method in the sub class called. Which makes me really confused about this. Anyone can please explain me what happens here when I use a different constructor to instantiate an Object.


Solution

  • At runtime the JVM knows that your s variable is a "SubClass" and thus can call the right (overwritten) method.

    The problem you're having is at compile time though. The compiler tries to verify your program to make sure that you didn't make any mistakes. It has no idea about the types of variables except the type you're telling it about.

    // the Java compiler remembers that there is a variable called 's' and that
    // it has the type 'SuperClass'. Note that the compiler does not check the
    // actual type of the instance. It just checks to see if the assignment is
    // is valid. Since the right side is of type 'SubClass' and the left side
    // has the type 'SuperClass' which is a parent of 'SubClass' this assignment
    // is valid for the compiler. But it ONLY remembers that 's' is of type
    // 'SuperClass' since that is what you told it about 's'.
    SuperClass s = new SubClass();
    
    // Here the compiler sees 's' and looks up its type. The type of 's' is
    // 'SuperClass' as remembered earlier. javac will no go and look up the
    // definition of 'SuperClass' and finds out that 'SuperClass' does not
    // have a method with the name 'subClass' so you get a compiler error.
    s.subClass();
    

    The reason that the compiler does this is, that you told the compiler 's' is of SuperClass type. So, any assignment of anything that extends SuperClass is a valid assignment for 's'. Theoretically you could assign a SuperClass to 's'. If the compiler would not perform this check you would be able to write code that calls methods on objects that might not have those methods, which would lead to a runtime error. Having an error like that randomly come up during runtime is much worse than the compiler just checking all the assignments and calls since you can fix those immediately and the runtime errors are sometimes hard to find and fix.

    As others have pointed out you can tell the compiler about your 's' actually being a 'SubClass' later on by casting it:

    ((SubClass) s).subClass();
    

    With this you basically tell the compiler: "I know that s is actually a 'SubClass', so please treat it like one for this part". If you're somehow wrong about that though and your 's' is actually a 'SuperClass' during runtime you will get a 'ClassCastException' which is a runtime error, since the JVM does not know what to do at this point.