"Amigo, do you like whales?"
"Whales? Nope, never heard of them."
"It's like a cow, only bigger and it swims. Incidentally, whales came from cows. Uh, or at least they share a common ancestor. It doesn't matter."
"Listen up. I want to tell you about another very powerful tool of OOP: polymorphism. It has four features."
1) Method overriding.
Imagine that you've written a "Cow" class for a game. It has lots of member variables and methods. Objects of this class can do various things: walk, eat, sleep. Cows also ring a bell when they walk. Let's say you've implemented everything in the class down to the smallest detail.
Then suddenly the customer says he wants to release a new level of the game, where all actions take place in the sea, and the main character is a whale.
You started to design the Whale class and realize that it's only slightly different than the Cow class. Both classes use very similar logic, and you decide to use inheritance.
The Cow class is ideally suited to be the parent class: it already has all the necessary variables and methods. All you need to do is add the whale's ability to swim. But there's a problem: your whale has legs, horns, and a bell. After all, the Cow class implements this functionality. What can you do?
Method overriding comes to the rescue. If we inherit a method that does not do exactly what we need in our new class, we can replace the method with another one.
How is this done? In our descendant class, we declare the method that we want to change (with the same method signature as in the parent class). Then we write new code for the method. That's it. It's as if the parent class's old method doesn't exist.
Here's how it works:
Code | Description |
---|---|
|
Here we define two classes: Cow and Whale . Whale inherits Cow .
The |
|
This code displays «I'm a cow» on the screen. |
|
This code displays «I'm a whale» on the screen |
After it inherits Cow
and overrides printName
, the Whale
class actually has the following data and methods:
Code | Description |
---|---|
|
We know nothing about any old method. |
"Honestly, that's what I was expecting."
2) But that's not all.
"Suppose the Cow
class has a printAll
, method that calls the two other methods. Then the code would work like this:"
The screen will show:
I'm white
I'm a whale
Code | Description |
---|---|
|
|
|
The screen will show: I'm white I'm a whale |
Note that when the Cow class's printAll () method is called on a Whale object, the Whale's printName() method will be used, not the Cow's.
The important thing is not the class the method is written in, but rather type (class) of the object on which the method is called.
"I see."
"You can only inherit and override non-static methods. Static methods are not inherited and therefore cannot be overridden."
Here's what the Whale class looks like after we apply inheritance and override the methods:
Code | Description |
---|---|
|
Here's what the Whale class looks like after we apply inheritance and override the method. We know nothing about any old printName method. |
3) Type casting.
Here's an even more interesting point. Because a class inherits all the methods and data of its parent class, an object of this class can be referenced by variables of the parent class (and the parent of the parent, etc., right up to the Object class). Consider this example:
Code | Description |
---|---|
|
The screen will show: I'm white. |
|
The screen will show: I'm white. |
|
The screen will show: Whale@da435a. The toString() method is inherited from the Object class. |
"Good stuff. But why would you need this?"
"It's a valuable feature. You'll understand later that it is very, very valuable."
4) Late binding (dynamic dispatch).
Here's what it looks like:
Code | Description |
---|---|
|
The screen will show: I'm a whale. |
|
The screen will show: I'm a whale. |
Note that it is not the type of the variable that determines which specific printName method we call (that of the Cow or the Whale class), but rather the type of object referenced by the variable.
The Cow variable stores a reference to a Whale object, and the printName method defined in the Whale class will be called.
"Well, they didn't add that for the sake of clarity."
"Yeah, it's not that obvious. Remember this important rule:"
The set of methods you can call on a variable is determined by the variable's type. But which specific method/implementation gets called is determined by the type/class of the object referenced by the variable.
"I'll try."
"You'll run into this constantly, so you'll quickly understand it and never forget."
5) Type casting.
Casting works differently for reference types, i.e. classes, than it does for primitive types. However, widening and narrowing conversions also apply to reference types. Consider this example:
Widening conversion | Description |
---|---|
|
A classic widening conversion. Now you can only call methods defined in the Cow class on the Whale object. The compiler will let you use the cow variable only to call those methods defined by the Cow type. |
Narrowing conversion | Description |
---|---|
|
A classic narrowing conversion with a type check. The cow variable of type Cow stores a reference to a Whale object. We check that this is the case, and then perform the (widening) type conversion. This is also called type casting. |
|
You can also perform a narrowing conversion of a reference type without type-checking the object. In this case, if the cow variable is pointing at something other than a Whale object, an exception (InvalidClassCastException) will be thrown. |
6) And now for something tasty. Calling the original method.
Sometimes when overriding an inherited method you don't want to entirely replace it. Sometimes you just want to add a little bit to it.
In this case, you really want the new method's code to call the same method, but on the base class. And Java let's you do this. This is how it's done: super.method()
.
Here are some examples:
Code | Description |
---|---|
|
|
|
The screen will show: I'm white This is false: I'm a cow I'm a whale |
"Hmm. Well, that was some lesson. My robot ears almost melted."
"Yes, this isn't simple stuff. It's some of the most difficult material you'll encounter. The professor promised to provide links to materials from other authors, so that if you still don't understand something, you can fill in the gaps."
GO TO FULL VERSION