5

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.

Shamal Sandeep
  • 263
  • 1
  • 2
  • 9
  • You talk about "using different constructor", but the only constructor defined for both classes is the default constructor. You are using two different classes to instantiate an object. You have some terminology mixed up. – Roger Gustavsson May 19 '15 at 08:52
  • there is no constructor at all,these are default methods since u added return type void to them. –  May 19 '15 at 09:39
  • @dubey-theHarcourtians what I meant by constructor is the classname next to new keyword in object instantiation – Shamal Sandeep May 19 '15 at 10:07

9 Answers9

4

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.

mhlz
  • 3,497
  • 2
  • 23
  • 35
3

You have declared your instance as an instance of SuperClass, so it will behave as such.

If you want to call subClass, there are two options:

  1. ((SubClass)s).subClass();
  2. Declare it as a SubClass: SubClass s = new SubClass();

Either way, you need to inform the JVM that you want it to work with the behavior of a SubClass, not a SuperClass.

Stultuske
  • 9,296
  • 1
  • 25
  • 37
3

You create a SuperClass object:

SuperClass s = new SubClass();

This is also an instance of SubClass, but to use all features of SubClass that SuperClass does not have, you must cast it or create a SubClass:

SubClass s = new SubClass();

To better understand this, think about a person and an architect that builds things:

Person a = new Architect();
a.build();  // ERROR! not every person knows how to build!


Architect a = new Architect();
a.build(); // SUCCESS!
Greg
  • 9,068
  • 6
  • 49
  • 91
Jordi Castilla
  • 26,609
  • 8
  • 70
  • 109
3

First of all, you are not asking about different constructors of the same class. You are asking about different constructors of different classes - SubClass and SuperClass.

Second of all, what you do in the second snippet is called overriding, not overloading.

In Java there's a difference between the compile time type of a variable and the runtime type of that variable.

Here :

SuperClass s = new SubClass();

The compile time type is SuperClass but the runtime type is SubClass. You are only allowed to call methods declared in the compile time type. However, if the runtime type overrides a method that exists in the compile time class, the runtime class's method will be called at runtime.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • Is it possible to override the `constructors` in java ? – Santhosh May 19 '15 at 08:52
  • 1
    @SanKrish No. You can define in a sub-class a constructor with the same arguments as the constructor of the base class, but that's not overriding, since when you call `new X()`, the constructor of class X will always be called in runtime. – Eran May 19 '15 at 08:55
  • @Eran So when he instantiates the sub-class , he is able to call the method defined in it – Santhosh May 19 '15 at 08:58
  • +1 for "In Java there's a difference between the compile time type of a variable and the runtime type of that variable." – underdog May 19 '15 at 09:56
1

First of all the methods you have written are not constructors because they have return type and they are not called as the class (capital letter). The constructor must have the same name as the class and no return type.

Other than that to your questions. This code works:

 SuperClass s = new SubClass();
 s.superClass(); 

Because the s variable is defined as SuperClass and the method s.superClass() is defined in the SuperClass class (it doesn't matter it is actually an instance of the SubClass because SubClass is also a SuperClass.

 SuperClass s = new SubClass();
 s.subClass();

For the same reason here it doesn't work. The variable has type SuperClass and the SuperClass doesn't have subClass method. If you want to use the variable as SubClass instance then you should do:

   SubClasss = new SubClass();
   s.subClass();
Veselin Davidov
  • 7,031
  • 1
  • 15
  • 23
0

It is because you instantiate your s as an object of SubClass when you new it like : s = new SubClass()

Also in your first case you have to cast your s object to SubClass because compiler didnt know that s is from type SubClass something like : ((SubClass)s).subClass()

Lrrr
  • 4,755
  • 5
  • 41
  • 63
0

If the reference is of the SuperClass type, Java's compile does not "know" that the runtime value stored in it is a SubClass, and hence doesn't allow you call SubClass's methods. You could, however, explicitly "tell" the compiler that this is a SubClass by casting, in order to gain access to these methods. Note that casting like this does not, in any way, change the actual object - it just provides more information to the compiler regarding what can and can't be called:

SuperClass s = new SubClass();
((SubClass) s).subClass();
Mureinik
  • 297,002
  • 52
  • 306
  • 350
0

Your problem (I think) is in this line...

SuperClass s = new SubClass();

The object you're creating is of type Subclass, BUT the variable s that you're putting it in is of type SuperClass, so it doesn't see about the subClass() method.

In your second example, the superClass() method in SubClass overrides (think of it as replaces) the SuperClass method of the same name.

Becaues SuperClass has a method of that name, you can call that method on s just fine.

Because variable s holds an instance of SubClass, it calls that version of the method.

I hope this helps. It seems confusing now, but there will come a time, when you'll wonder how you ever found it difficult - Just stick with it :)

Phil Anderson
  • 3,146
  • 13
  • 24
0

When you instantiate an object using new keyword, regardless of your reference, the object is created of the class that is used in the new keyword.

In this case : When you did this SuperClass s = new SubClass();, it means that s is capable of holding a SuperClass object, however, when you say new SubClass(), it actually created an object of class SubClass.

And now you are calling a method of class SuperClass by SuperClass s = new SubClass(); which is leading to this error, because your actual object is not of type SuperClass and hence you can not invoke it's method(s).

Soumitri Pattnaik
  • 3,246
  • 4
  • 24
  • 42