This week's book giveaway is in the OCPJP forum. We're giving away four copies of OCA/OCP Java SE 7 Programmer I & II Study Guide and have Kathy Sierra & Bert Bates on-line! See this thread for details.
I believe that it's actually essential for your brain to melt once or twice before you can really say that you understand polymorphism so don't worry about it - you're right on track. So, that said, let's see if we can put the pieces back together.
The real tricky part you have here is that you're mixing polymorphic method invocations (overridden methods) with variable hiding. I think you're just making it harder than it needs to be. Let me start with a real simple example of polymorphism and we'll go from there. Consider the following heirarchy:
What do you think that will output? Granted, I wrote this on the fly so I might have some typos, but the output should be:
Eating dog food. Drinking milk.
You see, when you override a method, Java determines, at run time, which method to invoke. The reference variable a1 refers to a Dog object. Therefore, at run time, the eat() method of the Dog class is invoked. Likewise with the reference variable a2 - at run time, the eat method of the Cat class is invoked.
That's polymorphism, plain and simple. It's the capability of a child class to behave in its own unique way, without necessarily knowing what type of child you're even dealing with.
So what is variable hiding, which I mentioned earlier? Variable hiding occurs when you have a variable in the subclass with the same name as a variable in the superclass. You now have two variables with the same name. Which one gets used when you refer to it? Well, usually, it's the one closest to your current scope. Let's look at your example now.
With both Block A and Block B uncommented, you have a single variable named className, defined within Vehicle. Because TwoWheeler extends Vehicle, this member is inhereted. In such a case, when you instantiate TwoWheeler, it sets the single className variable to be "TwoWheeler". You essentially have this (I love pictures):
When you invoke the info method on t1, Java looks at the object and looks for a method that matches that name. Is there one? Well, no. TwoWheeler doesn't define a method named info. However, TwoWheeler does inheret from another class, Vehicle. So, seeing that, Java will look at the Parent class, Vehicle, for a method named info. Does it find one? Sure thing. The method is located and invoked, printing out the text, "TwoWheeler".
So, what happens if we uncomment Block A? Well, in such a case, we're adding a member variable to the TwoWheeler class that hides the one from the parent class. It doesn't eliminate the member variable in the parent class, it only hides it. So, after creating your two objects, you now have this:
You see, when you instantiate a subclass, you must actually instantiate the superclass, first. That way, any initialization performed by the superclass can be done. So, before the TwoWheeler constructor runs, the Vehicle constructor runs and that constructor sets Vehicle.className to "Vehicle". Once that's done, the TwoWheeler constructor executes and sets the className variable to "TwoWheeler". But which one does it set? There are two variables with that name. Well, in this case, the one declared locally (within TwoWheeler) hides the other one. TwoWheeler.className gets set to "TwoWheeler" and nothing at all happens to Vehicle.className.
So what happens when you invoke t1.info()? Well, like I said before, Java will look into the TwoWheeler class for a method named info. Does it find one? Nope. So, Java looks into the Vehicle class for such a method. It finds it and it runs it. However, in that method, it wants to print out the contents of the variable, className. So which variable are we looking at this time? Well, this method is executing as if it were part of the Vehicle class (because that's where it's declared). Within that context, it knows nothing at all about TwoWheeler.className. So, it prints Vehicle.className, which contains "Vehicle".
Finally, what happens if we now uncomment Block B? Well, now we add an info method to the class TwoWheeler. Everything happens just as it did before, but now, when Java looks for an info method within TwoWheeler, it finds it. It's invoked from that point and, because we're now executing a method within TwoWheeler, we use the contents of TwoWheeler.className, rather than Vehicle.className.
So, does that help at all? Clear as mud? Let me know what confuses you.
**First of all, before I do anything else, let me say an ENORMOUS thank you for taking the effort to respond in such glorious detail. I totally appreciate it.
**As you say there were a couple of typos.
A) For anyone wanting to compile this:
"public void Cat extends Animal" should be "public class Cat extends Animal"
B) When you say "With both Block A and Block B uncommented, you have a single variable named className, defined within Vehicle", you mean "commented", not "uncommented", right?
**Now let me see if I get all your key points straight...
1) because we are invoking an overridden method, the method is selected at runtime based on the object, not the reference. Good.
2) I totally get your cat and dog example. I appreciate that the method hiding combined with me not being sure about the idea of scope when it comes to an inherited member variable.
3) I understand that if there IS an info() method in TwoWheeler, when the method refers to this.className it is looking at the one in TwoWheeler, not the one in Vehicle. Good.
This is the tricky bit for me....
If there is ONLY className defined in Vehicle, then...forgetting about v1...when t1 is created and the super constructor (Vehicle()) runs followed by the constructor for TwoWheeler...
...is it the case that there is a Vehicle object (super instance) with a className of "Vehicle" AND a TwoWheeler object with a classname of "TwoWheeler" and can I change them both independently of each other? (That's wrong isn't it?)
Let me start with a different example - see the code below
printcar() in Car. this.data = Car printcar() in Car. super.data = Car
But why is that? Following your explanation I would have thought that super.data would be Vehicle2 as set by the Vehicle2 super constructor.
I owe you a pint by the way. Also Corey, don't feel like this is a lost cause. I am quite bright and will understand you shortly...famous last words.
Joined: Dec 09, 2000
In response to my own post above before seeing nany replies.
I think I see the light.
If Vehicle2 has a member, any subclass that "inherits" it can access it, but there are not TWO versions.
When the Vehicle2 constructor runs the member is set to "Vehicle".
Then when the Car constuctor runs it is set to "Car".
Ergo, this.data and super.data refer refer to the same thing. Please say this is right.
BUT, if the subclass, i.e. Car had its own 'data' member, then super.data and this.data are two different things, i.e. Vehicle2 and Car respectively.
I hope you see this before replying...fingers crossed.
Joined: Dec 20, 2001
Sounds like you're catching on pretty quickly. When it comes to inheritance, I like to think of it this way...
Car extends Vehicle2. Rather than internalizing everything within Vehicle2, a new instance of Vehicle2 is created and Car has access to all inhereted members within it.
Maybe that helps, maybe not. If it doesn't make sense, don't worry about it too much. Regardless, what you said in your last post is right on.
Joined: Dec 09, 2000
My dear Corey,
I must say you have made my day.
I do understand your last explanation.
Going back to the original code...which is oh-so-much-clearer to me now...I see that when info() is invoked on the subclass, the method is found in the superclass and the method is referring to its nearest scope which is the Vehicle's member "className".
So here's a new question for you:
System.out.println("I am a '" + this.className + "'");
...in info() (in the super class) prints out the value of the className of the instance of Vehicle, since "this" is referring to Vehicle at this point.
But...given the same environment...how can I refer to the TwoWheeler's className, at the same code point?
E.g. We have "super" to refer to the super class of "this", from "this".
So, how do we refer to "this" from "super".
Crikey - how many times can I ask the same question? :-)
P.s. I can assure you that I can only stay awake walking for four more hours tops...so I'll stop hassling you soon.
Joined: Dec 20, 2001
Honestly, when you're invoking a method from a parent class, I don't believe it's possible to directly access a membre of a subclass. Think of it this way - does the parent class know anything at all about the subclass? Of course not. A Cat knows about Animal, but not the other way around.
If you really wanted to access a member of a subclass from a superclass, you might try creating a getClassName method and overriding it in all classes. For example, in your very first example, if you had created a getClassName method in both Vehicle and TwoWheeler, when Vehcile.info() would try to print the className, it would retrieve it from the getClassName method, rather than invoking it directly. In such a case, it would behave just like your other print statement - you'd have a polymorphic method invocation that would dynamically acquire the subclass member. Does that make sense? That seemed like a lot of big words and I'm not entirely sure what I wrote.
Joined: Dec 09, 2000
Yes that does make sense.
I had mused over the same point...easy for subclass to look up because it had to come from the class it extends from, but since many classes can extend from the parent, how does could the, premiscuous, parent keep track of all of its offspring...
...but I was thinking that, maybe, behind the scenes, there was some fancy processing that could have said to the the super's method when it is called, because the method does not exist in the subclass..."Hey 'Dad'/'mother' remember me I am your subclass"...then if ever the 'sub' keyword (which does not exist) was used it would refer back to that information and thus its members.
But thanks for setting me straight that none of that is either A) not currently possible and B) will probably never, ever, be needed. :-)
Anywise, I TRULY, TRULY, thank you for all your answers and all your comment and I look forward to speaking to you soon.