Let's see how polymorphism can make our graphical programs easier to write...
What does this tell us about Java and programming?
From its roots, polymorphism means "many shapes" or "many forms". In computing, we use this term to refer to code that works with values of different types. A function or method that can receive arguments of different types is a polymorphic function. A variable that can hold values of different types is a polymorphic variable.
Some languages support pure polymorphism. For example, Scheme variables and function arguments can hold hold any values, so long as the code that uses them runs.
Java variables and method arguments are declared with types that limit the kinds of values they can hold. But Java still supports two kinds of polymorphism.
Consider the operator +. We use + to add numbers. We also use + to concatenate Strings. This is a form of polymorphism, because + works with values of different types. It is called operator overloading. Languages such as C++ allow the programmer to overload operators for the own types, but Java does not.
But this same idea applies to messages, too. In Java, methods are distinguished not only by their names but also by their argument "signatures". Consider constructors. Each class can have multiple constructors, as long as the methods take different sets of arguments.
Then consider our AssociationList class, first created in Homework 3:
This results in AssociationList having two methods named add:
This, too, is a form of polymorphism, because add() works with arguments of different types. It is called method overloading. Java allows programmers to overload method names for the own types.
Method overloading is one form of polymorphism supported by Java. It is a useful technique when implementing a single class, as different methods of the same name can respond to messages of the same name differently, depending on the arguments sent by the client code. But if this were all we could do polymorphically in Java, then we would still be quite limited. Client code would have to know the specific kind of object that it was working with at every step of the way.
Now consider our MultiBallWorld symphony. As we learned earlier, we can add active components such as Buttons and Sliders to Frames. Today, we learned that we can add them to a Panel, too. Do you think that the AWT duplicates the code for this functionality? It does not. Frame and Panel are both subclasses of Container. These two kinds of object respond to the same messages. This is polymorphism across classes, made possible by inheritance.
But it is even better. A Panel can hold other Panels, too! When we add components to a Panel, whether the component is a Button or another Panel, the containing Panel must keep track of these different kinds of objects in common variables and must send them all common messages such as paint(). And they all respond by painting themselves. The Button paints itself in the familiar way. The contained Panel paints itself by sending paint() messages to any components it contains!
This all works because Panel, like Frame, extends Container -- and Container extends Component. But a Panel can also contain other Component -- including Panels!
This is where the real power of polymorphism lies...
The design of the Component class hierarchy is an example of the Composite design pattern.
problem ... forces ... solution
This pattern is not specific to Panels, the AWT, Java, or even object-oriented programming. Composites in occur throughout Java, in non-OO programs, and even the real-world. Some other examples of the Composite pattern:
The fact that composites occur not only in code but also in the real world should not surprise us. Our programs tend to follow the Principle of Continuity, and we learn from how the world works and apply that knowledge in our programs.