|
The term ‘polymorphism’ literally means behavior pertaining “many” (poly) forms (morphs). You will find dozens of definitions of this term and they all can be correct based on the context. In OO context, it is usually about many forms of a behavior (i.e method). By doing so, the programming language is able to process objects (rather requests-to-object’s-behavior) differently depending on that object’s type or class. . The two popular techniques of achieving “many forms” (and also the advantages for having “many forms”) are method overloading and overriding[‡‡].
In java, polymorphism manifests it self in three forms:
1. Method overloading
Method overloading[§§] actually means method ‘name’ overloading. It is a technique used to define multiple methods with same name, but different parameter list. It enables you to have “many forms” of a method in you class. Whenever some object makes a request, one of these methods is called depending on what parameters are specified in that request.
For example Listing 2.1 illustrates a class Rectangle that may have two overloaded methods for area calculation.

|
|
Note that polymorphism is the ability of objects to react differently when presented with different information. In case of method overloading that information is parameters. The method name is “overloaded” because often that name is the most appropriate one. For example, technically we could very well have two methods with different names areaUsingHeightAndLength() and areaUsingDiagonalLength(), but as you can see it is better to use same name for group of methods doing same task at core. |
2. Method overriding** through inheritance
Unlike method overloading[***], method overriding involves redefining a method (in a subclass). The redefined (overriding) method has the same name, parameter list and return type as the original method. Methods are often redefined to specify specialized behavior. For example, let us assume a class named Shape representing geometrical shape. If Shape class provide area() method, the probable subclasses of Shape class (for ex. Circle, Rectangle) are likely to override it. The overriding methods would return ‘area’ that is calculated using the shape-specific formula. The reason you would do it is the code flexibility. Let us see how-
Assume that you have a GUI class that draw shapes. This class can now invoke the area method on objects of Shape( or its subclasses) without worrying about implementation as-
public void paint(Shape shape) {
// Drawing shape
// area can be calculated as shape.area();
//
}
|
|
When dealing with type hierarchies, you often want to treat an object not as the specific type that it is, but instead as its base type. This allows you to write code that doesn’t depend on specific types. In the shape example, methods manipulate generic shapes (like the paint() method in the GUI class) without respect to whether they’re circles, squares, triangles, or some shape that hasn’t even been defined yet. All shapes can be drawn, erased, and moved, so these methods simply send a message to the shape object; they don’t really worry about how the object copes with that message.
|
Now if you send a Circle object to this method, a circle will be drawn and shape.area() would invoke Circle class’s area method.
|
|
You may just wonder ‘how?’. In case of functional programming (i.e. non-OOP) , when a compiler comes across a code for method invoation, it generates a call to specific code (later the linker resolves it to the absolute address of that code). This is called early binding as the actual method-to-be-executed is decided at compile-time itself. But in OOP, when you send a message to an object, the code being called isn’t determined until run time. The compiler only ensures that the method exists and performs type checking on the arguments and return value, but it doesn’t know the exact code to execute until runtime. The runtime environment takes care of invoking correct method based on the type of the object. This is called as the late binding of a code. |
Two clear advantages of overriding draw() method is
1. Uniformity of API - You (as a user) need not remember several drawing methods to draw different shapes. When a draw() method is defined in Shape class (and overridden in its subclasses), it tells the users that any shape (and shape-like objects) can draw itself with this method. There is no need to worry about individual shapes, e.g. what is the method for drawing circle and so on.
2. Extensibility- You can easily add newer subclasses to Shape class without changing your client code in GUI class.
|
|
Method overriding is sometimes known as the ‘runtime polymorphism’ because among several (“many”) methods (for ex. the area methods defined in Shape and its subclsses) defined, the method-to-be-invoked is decided on the (runtime) type of the object. For example in shape.area(), the runtime environment will check for the actual type of object in shape variable and then invoke the area method of that type(class) at runtime. |
3. Polymorphism and Interface (Method overriding through interface)
In Shape example, the area() method is defined in Shape class for the sake of abstraction. Sometimes a certain behavior (described as group of methods) is so abstract that it does not have any implementation at all[†††]. For such behavior, Java provides a mechanism called as an Interface. You define an interface (only) describing such behavior with a group of methods. Various classes can implement this behavior by implementing the methods. Now the key here is that these classes can be from different class hierarchy, yet they are of common type (i.e. the type of that interface). For example, you can define an interface called Paddlable describing paddling behavior(). Now you may have two classes Boat, Cycle that may belong to different class hierarchy but both are still Paddlable. Hence if you have a class named Recreation with a method doActivity() as-
public void doActivity(Paddlable paddlable) {}
Now you can send a boat object as well as a cycle object to this method. This method does not worry about the actual type (“is it boat or bicycle?”) of the object but only if it can perform a certain behavior (“is it paddlable?”).
|
|
Earlier we saw that objects communicate by sending messages to each other. Sending a message to an object usually means invoking a method on that object. The sender object is the requesting the receiving object to perform some functionality. Over past few years, the term “Polymorphism” has evolved to actually mean that the sender object need not be aware of the exact class (i.e type) of the receiving object. It only needs to know if the receiving object can perform the expected behavior. In other words, the receiving object can belong to an arbitrary class, but the sender object only needs to be confirm (By checking its type-see chapter 10) if the another object can perform that particular behavior. |
We have already seen the advantages while discussing the examples of polymorphism. To summarize it- Polymorphism helps you write systems that are flexible and modification resistant. It allows a method to take on "many forms" which in turn makes it re-usable and therefore more flexible. In our earlier example of a base class Shape, polymorphism enables you to define different area methods for any number of derived classes, such as circles, rectangles and triangles. No matter what shape an object is, applying the area method to it will return the correct results.