Reading

Required: These notes!
Recommended: Java in a Nutshell, TO APPEAR

Overview

Recall that there are four basic mechanisms / ideas underlying object oriented programming:
  1. encapsulation
  2. information hiding
  3. inheritance
  4. polymorphism
Encapsulation (bundling together data and the functions that operate on that data) and information hiding (lanugage mechansims to enforce the separation of interface from implementation) "solved" the first of our three problems with Procedural Programming not giving us full separation of interface from implementation: the "what to do with structs" problem. We've covered both of these over the last few weeks. We've also covered inheritance as a mechanism to modify or extend functionality. It solves the second of the three problems with Procedural Programming not giving us full separation of interface from implementation: modifying or extending functionality without having to access implementation. Today we will start learning about polymorphism, which is the last of the four basic mechanisms/ideas behind object oriented programming. It solves our last "problem": how to allow multiple implementations for the same interface.

Inheritance: Following "is-a" to its logical conclusion

Let's consider our old friends Point and LabPoint. You can take a look at their full source code if you like, but let's focus on just two things: their constructors and their toString()'s.

For starters, let's consider a simple program involving only Point objects.

Hopefully you see that what this program does is instantiate two points [(3,4) and (5,2)], and then randomly choose one of the two and assign reference v to point to it. You run this program and you might see "3 4" printed out. Run it again, and you might see "5 2" instead. It's random.

Now, recall that inheritance is supposed to give us an "is-a" relationship, as in "an object of type LabPoint is-a Point". If that's true, then the variable u should be able to point to a LabPoint just as much as it can point to a Point. So let's try it. The following program, Ex2.java, is just like the earlier program, except that the second "Point" w we instantiate is actually the LabPoint (5,2) with label "A", rather than just the plain-old Point (5,2). The code compiles, which certainly proves that the compiler is at peace with the prospect of the variable u referencing a LabPoint rather than a plain-old Point.

Now, here's the $24,000 question: when I run this program and the random choice is to set u = w, what gets printed out? Do I get "5 2" — since u is declared as a reference to a Point — or do I get "5 2 A" — since the object u refers to is actually a LabPoint? The answer is (drum-roll please) ... "5 2 A"!
~/$ java Ex2
5.0 2.0 A
This is actually pretty amazing. "Why?", you ask? Because the the compiler didn't know which function was going to be called! It couldn't know, because ultimately which actual method gets executed depends (down to the millisecond) on when the use chooses to execute the program. Moreover, from one run to the next, without re-compiling, a different method gets executed. This is truly new. You have never before seen code where you couldn't tell exactly which function/method would get execute at a given function call site. In this example, it's only at run-time that we can determine which function to execute. BTW: The only sane way for this to work (and the way it does work) is for the virtual machine, when it comes to the site of the call u.toString(), to check the type of the object u refers to and allow that to determine which version of toString() gets executed.

A function call-site like this is called polymorphic. The word means "many shapes" and it's trying to get at the idea of many different functions may result from a single function call site. Another common term to refer to a call like this is as a dynamic function call. A function call for which the actual function to be executed can be determined at compile time is referred to as static, because it is "unchanging" from run-to-run of the program, whereas a call for which the question of which actual function to execute can only be determined as the program executes is called dynamic, because it changes from runt-to-run or even from call-to-call within a single program execution.

Polymorphism

Suppose we have a class Class1 and classes Class2, Class3, ...Classk, that are derived from Class1, either directly or through a chain of extends's. Suppose further that the base class Class1 defines a method method(Type1,Type2,...,Typen), and that many of the derived classes override this method. If a variable var is declared as a reference to the base class Class1, then when the call
var.method(arg1,...,argn)
is made, which actual method gets executed is based on the type of the object var currently points to. So, if var currently points to an object of class Classi, then the Classi version of the method is the one that actually gets called.

We are all instances of class Object

We can see a slightly more interesting example of a polymorphic function call if I let you in on a little secret: all classes are derived from a class called Object. Specifically, when you define a class without the "extends" keyword, that class implicitly "extends" Object. So the class Point above is an Object. Now you can look Object up in the Java API documentation, and you'll see that it has some methods. Most notably for us, it has the method "toString()". That means you can call .toString() for any object. So let's expand our example:

     Object
       |
       |       Note: This diagram shows our simple "class hierarchy",
     Point           i.e. that LabPoint extends Point, which in turn
       |             extends Object.  As we get more and more into
       |             using inheritance, the hierarchies become more
    LabPoint         complex and these diagrams become more important.
Fun note: the way println works is that it implicitly calls the toString() function on whatever argument you give it, so we could change the last line to
System.out.println(u);
... and have the exact same program!

Vocabulary break!

We need to make sure we're on the same page with our vocabulary so that we can talk meaningfully about what happens in programs. Some concepts have one name in Java, but a different name in the larger world of programming languages. I'll use the generally accepted terms here, but point out what the Java-esque equivalents would be.

Okay, Polymorphism is great ... so what do I do with it?

Hopefully you've got the idea that polymorphism is a really cool mechanism. But what should we do with it? It's a hammer, what's the nail? Well, polymorphism solves the last of the three "problems" that we had with Procedural Programming not fully separating interface from implementation: how to allow multiple implementations for the same interface.

Here I'm going to jump into a non-trivial example of how this does great things for us. Consider the following program, Ex5.java, which generates expense reports. It makes use of classes Expense and ReportGenerator, whose interfaces are shown below (if you want to see it, here is the full source code).

Here's an example run of this program, so you can see how it works.
~/$ java Ex5
Enter dollar amount for lodging (0 for none): 357.89
Enter dollar amount for meals (0 for none): 170.08
Enter dollar amount for incidentals (0 for none): 58.23

 $0357.89	lodging
 $0170.08	meals
 $0058.23	incidentals
---------
 $0586.20	total

Now imagine that we want to include mileage expenses, but we want the user to simply enter the mileage and the program to calculate the mileage expense based on a hard-coded rate. What we need is for expense to just work a little differently for mileage. It needs to ask for an amount of miles, and set the expense amount to the miles times the rate. How can we make some instances of Expense behave in this new and different way? Inheritance! We will create a RateExpense class and override ask() to take on this different behavior. The magic of polymorphism is that when the ReportGenerator calls the ask() method for an instance of RateExpense, we'll get the new version of ask().

Here's an example run of the new program.
~/$ java Ex6
Enter dollar amount for lodging (0 for none): 357.89
Enter dollar amount for meals (0 for none): 170.08
Enter dollar amount for incidentals (0 for none): 58.23
Enter amount of miles: 302

 $0357.89	lodging
 $0170.08	meals
 $0058.23	incidentals
 $0093.62	mileage (at 0.31 dollars per miles)
---------
 $0679.82	total
What you need to pay special attention to is how polymorphism made this work. The ReportGenerator class and its create() method were written and compiled before anyone thought about writing RateExpense, and yet we can feed it an array that contains RateExpense, and the system works perfectly. That's magic! Now, what does this have to do with our quest for perfect separation of interface and implementation? Well, the last thing we identified as being a limitation of procedural programming was its requirement that every interface (function prototype) have only one implementation. If you look at the "interface" for the Expense class, you'll see that Expense class and the RateExpense class both provide implementations for that interface. Specifically, RateExpense implements void ask(Scanner sc) differently than Expense. So, polymorphism gives us the last thing we wanted: multiple implementation of the same interface!