Polymorphism

Polymorphism

Overriding the base class

If the derived class has a method or data element with the same name as the base class, the thing in the derived class takes precedence. Let's look at a new version of class bar that has a method with the same name as one in class foo. The regular code file for this is here. First we look at the code for the parent class, foo. Then the code for the child class, bar. Finally, a driver program to show off the overridden methods, main.

This is a very important feature of Java and object oriented programming. Each of the classes has its own version of bigger() that is appropriate to it. The user only has to know that they should call bigger() on an instance and they will get the right value. If you need to access the version of the method that is part of the parent, you can use the super reference.

For example, the play() method in wind could call the version in instrument by this syntax.

super.play(n);
While it is possible to override the definition of data variables, this is not recommended. It can get very confusing which version you mean. It is possible to prevent child classes from overriding a method. If it is marked final, then the children must use the parent method.

Type Binding

One of the issues when using inheritance is what happens when you use a reference of the type of a class higher up the tree to hold an object of a class lower in the tree? If you use such a reference to access the methods on the class, do you get the parents version or the child version. The simple answer is you get the version of whatever the reference is pointing to.

Using a pointer to the base class, the program can point at a variety of objects of the derived classes and when the overridden function is called, it is the one for the type of the object being pointed at. The code for an example is here. There are three classes in the hierarchy here. The parent class (instrument) is here.
This has a child class .
Thegrandchild class is here.
Thedriver code is here.
Now let's look at the output.


Run the tune method on a wind instance
wind::play
Run the tune method on a reed instance
reed::play
Set music to flute and run play
wind::play
Set music to oboe and run play
reed::play
The function to be called is determined at run time (late binding). This technique allows us to write functions like tune() that will work for any derived class from a base class. It is easier to extend the classes because tune() still works for other classes derived from instrument. Like drum or guitar. It will also work for lower levels. If class brass is derived from wind, tune() still works. If there is no overridden functions, the one next up the hierarchy is used. Examine the brass class and a new driver to see this. Notice that when tune() is used on an instance of brass, the play() method of it's parent, wind, is used.

Run the tune method on a wind instance
wind::play
Run the tune method on a reed instance
reed::play
Run the tune method on a brass instance
wind::play
Set music to flute and run play
wind::play
Set music to oboe and run play
reed::play
Set music to cornet and run play
wind::play

A little more Polymorphism

Now let's walk through some of the example code from the book to see some of the same ideas in a more concrete example. The code for this is here and on the CD-ROM that came with the book.

Indirect method calls

If class B inherits from class A, it can call the public methods of A but not the private ones. However, if a public method of A uses a private member, the private member will be executed. Think of it this way, inside B is an instance of A. So just like I can call public methods of A from an instance of A, I can do it from an instance of B.

Interface hierarchies

Interfaces can extend other interfaces so you can build a parallel tree of interface inheritance just like classes. They don't intersect since classes can't inherit from interfaces and vice-versa.

Adapter classes, GUIs and Inheritance

The idea of inheritance comes up often in the use of Java GUI components. It is very common to extend a built in component to override some aspect of its appearance or behavior.

We have used listener interfaces to implement GUI actions. When we made a small class to implement the listener interface, we had to override all the methods of the interface, even the ones we weren't going to use. There is another way to handle this without cluttering up the code with a bunch of empty method definitions.

For most of the listener interfaces, there is a corresponding adaptor class. An adaptor class is very simple. It implements the interface we are interested in and provides empty methods for all the interface methods. We can make a class that extends the adaptor class, only overriding the methods we are interested in.

If we used the Interface, we would need to provide empty methods for all the methods in the interface that we didn't need.


   private static class GenericWindowListener implements WindowListener
    {
   //-----------------------------------------------------------------
   //  Terminates the program when the window is closed.
   //-----------------------------------------------------------------
        public void windowClosing (WindowEvent event)
        {     
            System.exit(0);
        }
        public void windowActivated (WindowEvent event){}
        public void windowClosed (WindowEvent event){}
        public void windowDeactivated (WindowEvent event){}
        public void windowDeiconified (WindowEvent event){}
        public void windowIconified (WindowEvent event){}
        public void windowOpened (WindowEvent event){}
    } // window listener
But with the adaptor class, we only need to override the method we are interested in. All the other methods in the interface already have empty methods provided for us in the class.

   private static class GenericWindowListener extends WindowAdapter
    {
   //-----------------------------------------------------------------
   //  Terminates the program when the window is closed.
   //-----------------------------------------------------------------
        public void windowClosing (WindowEvent event)
        {     
            System.exit(0);
        }
    } // window listener