aspose file tools*
The moose likes Programmer Certification (SCJP/OCPJP) and the fly likes Polymorphism Teaser Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of The Java EE 7 Tutorial Volume 1 or Volume 2 this week in the Java EE forum
or jQuery UI in Action in the JavaScript forum!
JavaRanch » Java Forums » Certification » Programmer Certification (SCJP/OCPJP)
Bookmark "Polymorphism Teaser" Watch "Polymorphism Teaser" New topic
Author

Polymorphism Teaser

Franz Fountain
Ranch Hand

Joined: Nov 15, 2006
Posts: 58
What is it about horses? You can lead them to water, but you can't make them drink!

Take a look at this code for example:



And now I'm going to show you the output

Horse eats
Horse doesn't drink packaged water
Horse at water
Animal drinks protected water
Animal doesn't drink private water


The horse is at the water, but it won't drink the water. Why?
Hint: Take a look at line #1.

For all you ranchhands - what happens when you untie your horse at line #2 and give him/her another chance to drink?

The winner gets a free Pony Ride! Wheeee!
[ November 26, 2006: Message edited by: Franz Fountain ]
Wes Misenheimer
Greenhorn

Joined: May 21, 2006
Posts: 11
Animal can't call a Horse method, even if it's overridden in Horse. Unless maybe you create a Horse object in Animal an call it using that Horse object reference.

i.e. Try replacing #1 with:
new Horse().drink(); // #1 - Which drink method gets called here? Why?

Of course then it's a new object with atWater=false.

#2 calls the Horse drink() method.
Wes Misenheimer
Greenhorn

Joined: May 21, 2006
Posts: 11
Oops, I was wrong in my last post. If you change the private drink() method in Animal to public, it does call the public drink() method in Horse. So, perhaps, since the method in Animal was marked private, the Horse class did not have access to it, and could not override it.
Prabhu Venkatachalam
Ranch Hand

Joined: Nov 16, 2005
Posts: 502

you have superclass(Animal) reference and drinkProtected(); method is not overridden in the subclass(Horse). That's reason it is calling super class drink() method.

But when you type cast(commented line in PolyMorph class) it again back to subclass(Horse), subclass version of drink() method is called.


hth,


Prabhu Venkatachalam<br />SCJP 1.4,SCWCD 1.4<br />prabhu.venkatachalam@gmail.com
Wes Misenheimer
Greenhorn

Joined: May 21, 2006
Posts: 11
Ok, let me try one more time... and I have a question.

Are the drink() methods considered overloads? (I don't think so)

The reason I ask is because they act just like overloads in the situations I've tested them in. They use the reference type to determine which drink()method to run. When drink() is called in Animal, the only way it can execute the Horse drink() method is to use a Horse type reference variable, or a reference variable that is cast to a Horse type.
Thanks for your patience!

I shortened the program and altered it for experimentation.

/*
The private drink() method in Animal and the public drink() method
in Horse are different methods. Not an override, but act very much like
an overload as far as I can tell. This is because Horse cannot see the
private drink() method in Animal to override it.
The reason I say they act like an overload is because the drink()method
that is actually run is determined by the reference variable type.
Try comment 1 below, and then comment 2, in calldrinks() below.
*/

class Animal {
private void drink() {
System.out.println("Animal drinks");
}
public void calldrink(Animal a) { //1-Change Animal to Horse - try it,
a.drink(); // 2-then cast a to Animal <((Horse)a).drink()>, try it.
}
}

class Horse extends Animal {
public void drink() {
System.out.println("Horse drinks");
}
}

class Test2 {
public static void main(String[] args) {
Horse a = new Horse();
a.calldrink(a);
}
}
Greg L Tonn
Ranch Hand

Joined: Nov 27, 2006
Posts: 52
I have a few questions as well.

1.) Do the below lines create two instances on the heap, one for the horse and one for the animal?


3.) If you change the drink method in Horse to private the cast of animal to horse doesn't work. Why is that, don't you get don't you get the private methods of the original class? Through testing it does not appear that way but I'm not sure why.

Thanks everyone, this is my first post!
Franz Fountain
Ranch Hand

Joined: Nov 15, 2006
Posts: 58
Hi Greg and let me be the first to welcome you to Javaranch!

Now to your questions.


1.) Do the below lines create two instances on the heap, one for the horse and one for the animal?
code:
class Horse extends Animal

// class header and main method
Animal a = new Horse();
// close main and class


This creates an Object/Instance of a Horse (on the heap) but it does also create a Animal reference variable (on the stack). The reference variable is assigned to refer to the Horse object.


3.) If you change the drink method in Horse to private the cast of animal to horse doesn't work. Why is that, don't you get don't you get the private methods of the original class? Through testing it does not appear that way but I'm not sure why.


I'm not sure what you mean by "the cast of animal to horse doesn't work". As to the other question "Why is that, don't you get don't you get the private methods of the original class?", this is the big question that is being explored here. Take a closer look at the access modifiers and there definitions in Ch. 1 of K&B (Kathy Sierra and Bert Bates book). On p. 30 it says a subclass can't inherit a private member. So that includes methods.

In this case drink() is a private method of Animal, so it can't be inherited by Horse.

By the way, what access modifier would you use to create a method that can be inherited by any subclass but can't be seen by any other classes?
Prabhu Venkatachalam
Ranch Hand

Joined: Nov 16, 2005
Posts: 502


Are the drink() methods considered overloads? (I don't think so)

drink() is nethier overlaoded nor overrided. JVM will consider this as a new method and calling method will be decided in compile time.(i.e) method will be called accroding to the reference type.

JVM while deciding which method to call, it search in down-top approach(end sub-class to super class). This happens only for overridden methods. but for overloaded methods it will be decided in the compile time.

In this case(I don't know what to call it), It is not following overridden as well as overloaded rules and it will consider as a new method. Method call will be decided in compile time.



1.) Do the below lines create two instances on the heap, one for the horse and one for the animal?



only one instance and assign it to superclass reference.


3.) If you change the drink method in Horse to private the cast of animal to horse doesn't work. Why is that, don't you get don't you get the private methods of the original class? Through testing it does not appear that way but I'm not sure why.



You cannot access private members outside the class, even using its instance.


what access modifier would you use to create a method that can be inherited by any subclass but can't be seen by any other classes?


protected


hth,
Prabhu Venkatachalam
Ranch Hand

Joined: Nov 16, 2005
Posts: 502


Are the drink() methods considered overloads? (I don't think so)

drink() is nethier overlaoded nor overrided. JVM will consider this as a new method and calling method will be decided in compile time.(i.e) method will be called accroding to the reference type.

JVM while deciding which method to call, it search in down-top approach(end sub-class to super class). This happens only for overridden methods. but for overloaded methods it will be decided in the compile time.

In this case(I don't know what to call it), It is not following overridden as well as overloaded rules and it will consider as a new method. Method call will be decided in compile time.



1.) Do the below lines create two instances on the heap, one for the horse and one for the animal?



only one instance and it is assign to superclass reference.


3.) If you change the drink method in Horse to private the cast of animal to horse doesn't work. Why is that, don't you get don't you get the private methods of the original class? Through testing it does not appear that way but I'm not sure why.



You cannot access private members outside the class, even by using its instance.


what access modifier would you use to create a method that can be inherited by any subclass but can't be seen by any other classes?


protected


hth,
[ November 27, 2006: Message edited by: Prabhu venkatachalam ]
Franz Fountain
Ranch Hand

Joined: Nov 15, 2006
Posts: 58
Hi Wes,

In your first reply you said

Animal can't call a Horse method, even if it's overridden in Horse.


I think you meant
Animal can't call a Horse method, unless it's overridden in Horse.

We're exploring polymorphism here and polymorphism requires that a method be overriden. It also requires that a superclass reference variable be used to refer to a subclass object. That's why Horse is assigned to an Animal reference variable.

In your second reply you said


Oops, I was wrong in my last post. If you change the private drink() method in Animal to public, it does call the public drink() method in Horse. So, perhaps, since the method in Animal was marked private, the Horse class did not have access to it, and could not override it.


Yes the drink() method in Animal is marked private. So does the Horse class have access to it? That's the question. If Horse doesn't have access, then can it override drink()?

On to your third reply


Are the drink() methods considered overloads? (I don't think so)

The reason I ask is because they act just like overloads in the situations I've tested them in. They use the reference type to determine which drink()method to run. When drink() is called in Animal, the only way it can execute the Horse drink() method is to use a Horse type reference variable, or a reference variable that is cast to a Horse type.


So is the drink() method in Horse an override or an overload of the drink method in Animal? That's the essential question here. It has the same signature (same name, same parameters, same return type) so it looks like a valid override. But, oops, the same method in Animal is marked private.

As you have seen through your experimentation, the drink() method in Horse acts like an overload. The reason has to do with the private nature of the Animal drink() method. Look at K&B Ch. 2 p. 102-103 for rules on overriding. What does it say about private methods?

Here is your experiment which does indeed simplify part of the original question.


class Animal {
private void drink() {
System.out.println("Animal drinks");
}
public void calldrink(Animal a) { //1-Change Animal to Horse - try it,
a.drink(); // 2-then cast a to Animal <((Horse)a).drink()>, try it.
}
}

class Horse extends Animal {
public void drink() {
System.out.println("Horse drinks");
}
}

class Test2 {
public static void main(String[] args) {
Horse a = new Horse();
a.calldrink(a);
}
}


The questions I have for you regarding line 2 above are:
What method does the compiler think is being called?
and
What method does the JVM think is being called (at runtime)?
and
How do these differ for overriding (when polymorphism is in effect) and overloading?
and finally
Is there any polymorphism associated with overloading?

These are big OO questions and the model you use in your brain to visualize overriding and overloading will determine your answers. In my case I found that my model needed a bit of tweaking when I looked at this rather simple "Horse drink" code.
Franz Fountain
Ranch Hand

Joined: Nov 15, 2006
Posts: 58
Hi Prabhu,

Looks like you already understand this question. You get the free Pony ride!
weee

Franz
Wes Misenheimer
Greenhorn

Joined: May 21, 2006
Posts: 11
Hi Franz,

I've included a copy of the program below my answers.
And below that are some more detailed descriptions of what I
think is true.

Thanks, very much, for your help!!!

Wes

Answers to your questions:

What method does the compiler think is being called?
Ans. Animal's drink(), since it can't be overridden.

What method does the JVM think is being called (at runtime)?
Ans. Animal's drink() - decided by the compiler.

How do these differ for overriding (when polymorphism is in effect)
and overloading?
Ans. -Overriding uses the object type to determine which
method() to call.
It is chosen by the JVM at run-time.
-Overloading uses the reference variable type.
It is chosen at compile-time.

Is there any polymorphism associated with overloading?
Ans. No.

class Animal {
private void drink() {
System.out.println("Animal drinks");
}
public void calldrink(Animal a) { //1
a.drink(); // 2
}
}

class Horse extends Animal {
public void drink() {
System.out.println("Horse drinks");
}
}

class Test2 {
public static void main(String[] args) {
Horse a = new Horse();
a.calldrink(a);
}
}

1-Assume that the Animal drink() method is public, and is overridden
in Horse. The compiler sees that it could be overriden, and inserts
code that allows for the JVM to check the ACTUAL OBJECT TYPE and
determine which version of drink is to be called. If you pass
calldrink() an Animal object, it calls the Animal version of drink().
Pass calldrink() a Horse object, and it calls the Horse version of
drink().

Overridden methods use the type of the actual object to determine which
method to call - not the reference type.
This happens at run-time (JVM decision).

2-In the situation where the method() drink is overloaded, such as the
examples on pp.108 - 110 of K&B SCJP Java 5 book, the method() is chosen
by the reference type, rather than the actual object type. Overloaded
methods use the reference type to determine which method to call - not
the actual object type.

Overloaded methods are chosen at compile-time (Compiler decision).

3-As in #1 above, but assuming that the drink() method in Animal is
marked private,
a)The drink() methods in Animal and Horse are not overrides,
because Horse can't see the drink() method in Animal to override
it.
b)They are not really overloads, because the only difference is the
access modifier.
c)The compiler sees the method marked private, and knows that it
cannot be overridden, so it chooses the Animal version of
drink() to implement at compile time.
d)The only way to call the Horse version of drink() is to
explicitly cast the call to a.drink() as a horse reference,
((Horse)a).drink(), or in some way cause the reference
type for 'a' to be a Horse type.
This is still a compile time decision.
Franz Fountain
Ranch Hand

Joined: Nov 15, 2006
Posts: 58
Excellent Wes

Here are some scattered thoughts in reply.

I think there is a subtlety in that the compiler always acts as if there is no overriding in effect in determining whether a method is valid or not. Only methods of the reference type are allowed to compile.

I don't know if a private method causes the compiler to create optimized code that does not allow polymorphism (overriding). In C++ you actually have to mark a method as "virtual" in order to get polymorphism. In Java polymorphism is ON by default. The reason for the C++ decision is that polymorphism has a negative performance effect and C++ is less OO (Object Oriented) in this respect. So I don't know if the compiler optimizes the code at compile time or if the JVM realizes at runtime that the method is private and then does the right thing.

Regarding overloading, there is a kind of "poly-something" going on. It's a static choice of methods based on the parameters. It's not as whizbang as runtime polymorphism which is a dynamic choice of methods, but it's still an interesting feature of the language and not trivial.

Prabhu makes a very good and interesting point in saying that the drink() method is neither overriden nor overloaded by Horse. It is simply a new method with the same name as the method in Animal. It could have been called eat() and the effect would be the same. There is no relationship between the 2 drink methods of Animal and Horse because of the private label in Animal. This is because Horse never sees the drink method at all and is completely unaware that such a method even exists. The drink() method in Animal is totally hidden from view by the private access modifier. It's so hidden that the compiler is fooled into thinking that Horse just came up with the drink() method name all on it's own. I'm actually surprised that this doesn't produce an error or at least a warning, but there is no complaint at all from the compiler.

I also asked a question "what access modifier would you use to create a method that can be inherited by any subclass but can't be seen by any other classes?" Prabhu said protected, but I disagree. A protected method is visible to any class in the package. In fact protected is less restrictive than the default access modifier (which is sometimes called "friendly", but I prefer to call it package access.) Protected just adds "subclasses that are not in the package" to the list of classes that can see a method (or variable). The answer is that there is no such access modifier. My best guess if I didn't know anything about Java or OOP would be that "private" would be the access modifier that would answer the question. But as we've learned, private is very restrictive. (In C++ protected would be the correct answer because C++ doesn't have the concept of package.)

You know there's still another issue lurking in this example.

Why does the horse refuse to drink private water? Why is the output "Animal doesn't drink private water" instead of "Animal drinks private water"? After all the Horse is at the water, why not drink?

(I have a feeling I've missed something important in my reply but I can't think of what it is. Oh well I suppose someone will point it out.)
Wes Misenheimer
Greenhorn

Joined: May 21, 2006
Posts: 11
Yes, there is the issue of the two atWater variables.
Since they are both marked private they are separate variables,
accessible only within their own respective classes.
Franz Fountain
Ranch Hand

Joined: Nov 15, 2006
Posts: 58
Hi Wes,

Yes. That's it.

Cheers,
Franz
 
Consider Paul's rocket mass heater.
 
subject: Polymorphism Teaser