Reading

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

An interesting example

Consider the following two classes, which might be used to write programs that process information on the yard. They might be used in the same program, or perhaps in different programs.

Note: here are two classes for testing out Mid and Prof: TestMid and TestProf.

If you look at these two classes, you immediately see that there's a lot of duplication between them. Hopefully by now you have enough of an Object Oriented mindset that you immediately think of inheritance. However, we can't really derive Mid from Prof, because Prof has some things (like "department") that Mid doesn't. More philosophically, we don't have an "is-a" relationship. It's not true that a Mid "is-a" Prof. On the other hand, we can't derive Prof from Mid for all the same reasons. So we appear to be stuck.

Using inheritance to express commonality

The solution to the above problem is to pull out of both Mid and Prof what they have in common, and make that a base class that both Mid and Prof can extend. I think we can all agree that Mids and Profs are both examples of people, so we will call this new common base class "Person".

Notice that we had to manufacture a new "title()" function for class Person. You see, both Mid and Prof have "title()" functions, but both are specific to one class or another. However, the "fullName()" method requires calling the "title()" function, so we need to have a "title()" function in the class Person. Otherwise, we literally just pulled the common methods and fields out of the two original classes. At any rate, with the commonality factored out, we can redefine Mid and Prof as classes that extend Person.

Question: So what, other than the happy feeling of being object=orientedy, have we gained? First of all, don't underestimate that happy feeling. Secondly, we have many concrete benefits. If we want to also create a class for coaches, we can now do that with a minimum of extra work. More importantly, however, is that we can now deal nicely with collections that mix Mids and Profs. Why? Because we can have an array (or list) of Person references, each of which can point equally well to both Mids and Profs, and all of the methods that are defined for both Mids and Profs can be called on the array elements and polymorphism will take care that the function that's appropriate for the actual type of the object the array element points to is called. Here's a simple example of such a program:

Class Hierarchies

The classes Person, Mid and Prof form what's called a class hierarchy, with Person at the top of the hierarchy and Mid and Prof directly underneath the class Person. There is a natural graphical depiction of the relationship between Person, Mid and Prof:
	Person
         /  \
        /    \
      Mid    Prof
Often in designing an object oriented program we start off by sketching out one or more class hierarchies that we'll want to create. That's how important and fundamental class hierarchies are to object oriented programming. These hierarchies can get more complex too. If the program had to have separate facilities for dealing with Mids that were varsity atheletes and also for mids in musical groups (e.g. glee club or drum and bugle) the hierarchy might grow like this:
           Person
            /  \
           /    \
         Mid    Prof
         / \
        /   \
 VarAthMid  MusicMid
This would mean that Mid and Prof both extend Person, and that VarAthMid and MusicMid both extend Mid.

Casting, and why you should normally not do it

Casting is converting an object from one type to another. In the case of Java, casting objects (as opposed to primitive values) is really just reinterpreting. If you have a Mid m that's been assigned an object, you are free to "cast" m to type Person with the expression (Person)m. Not that this does much, since m literally "is-a" Person by virtue of inheritance. The other direction is scarier. If I have Person p can I cast it to a Mid? The answer is "yes" (you do it with (Mid)p, of course), but it's a qualified "yes". You see, the cast can fail if the object that p points to is not actually a Mid; if, for example, it is a Prof instead. (Clearly you cannot make a Mid out of a Prof!) Java forces you to deal with the possibility of this failure using its "exceptions" mechanism, which we haven't covered yet. So, for today take it on faith that you do the conversion like this
Mid m = null;
try { m = (Mid)p; } catch(Exception e) { }
... recognizing that the cast may fail, with the result that m is not assigned a new value so that, in this example, it would stay null. In some sense, this gives you a way to check the type of object p points to, and do different things according to the result. As it turns out, this is seldom a "good" object oriented programming move!

Continuing with our Person/Mid/Prof example, let's suppose that we want to print out each person's full name and their "affiliation", which will be "company" for Mids and "department" for Profs. We could handle this by casting our Person objects to Mid or Prof as appropriate, and using their getCo() and getDept() methods to get the affiliation. Here's an example of how:

Although this works, it is not good OOP practice. Objects should act in the manner appropriate to themselves, as a rule — which is accomplished through polymorphism. To let the objects "act for themselves", however, we have to have a method in the base class that the various derived classes can override in order to "act for themselves". In fact, we already have that behavior with the "fullName()" method. So, to do the same for "affiliation", we need to put an "affiliation" method in the base class "Person". If we do that, and override the affiliation() method in Mid and Prof, the print loop becomes a simple, beautiful (and very OOPy):
// print
for(int i = 0; i < n; i++)
{
  System.out.println(A[i].fullName() + " - affiliated with " + A[i].affiliation());	
}
Almost always, if you catch yourself wanting to figure out the actual type of an object and to "cast" to that actual type, it is an indication that you need to add one or more methods to the base class and override them appropriately in the derived classes.