<- Previous Page Next Page ->

Polymorphism:

Polymorphism means that different objects respond distinctively to the same message. For example, when you send the same message, ‘cost’ to a spike-bicycle object, mono-cycle object and tandem bicycle object, each one responds appropriately. All of these cycles of the class bicycle have its own individual price.

Polymorphism plays an important role in allowing objects having different internal structures to share the same external interface. This means that a general class of operation may be accessed in the same manner even though specific actions associated with each operation may be accessed in the same manner even though specific actions associated with each operation may differ. Polymorphism is extensively used in implementing inheritance.

Polymorphism allows an entity (for example, variable, function or object) to take a variety of representations. Therefore we have to distinguish different types of polymorphism which will be outlined here.

The first type is similar to the concept of dynamic binding. The concept of dynamic binding allows a variable to take different types dependent on the content at a particular time. This ability of a variable is called polymorphism. Another type of polymorphism can be defined for functions. For example we will now look through the coding of C, suppose you want to define a function is Null () which returns TRUE if its argument is 0 (zero) and FALSE otherwise. For integer numbers this is easy:

  boolean isNull(int i) {
    if (i == 0) then
      return TRUE
    else
      return FALSE
    endif
  }

However, if we want to check this for real numbers, we should use another comparison due to the precision problem:

  boolean isNull(real r) {
    if (r < 0.01 and r > -0.99) then
      return TRUE
    else
      return FALSE
    endif
  }

In both cases we want the function to have the name is Null. In programming languages without polymorphism for functions we cannot declare these two functions because the name is Null would be doubly defined. Without polymorphism for functions, doubly defined names would be ambiguous. However, if the language would take the parameters of the function into account it would work. Thus, functions (or methods) are uniquely identified by:

·         The name of the function (or method) and

·         The types of its parameter list.

Since the parameter list of both is Null functions differ, the compiler is able to figure out the correct function call by using the actual types of the arguments:

  var i : integer
  var r : real
 
  i = 0
  r = 0.0
 
  ...
 
  if (isNull(i)) then ...   /* Use isNull(int) */
  ...
  if (isNull(r)) then ...   /* Use isNull(real) */

If a function (or method) is defined by the combination of

·         its name and

·         the list of types of its parameters

we speak of polymorphism. This type of polymorphism allows us to reuse the same name for functions (or methods) as long as the parameter list differs. Sometimes this type of polymorphism is called overloading.

The last type of polymorphism allows an object to choose correct methods. Consider the function move() again, which takes an object of class Point as its argument. We have used this function with any object of derived classes, because there is-a relation holds.

Now consider a function display() which should be used to display draw able objects. The declaration of this function might look like this:

  display(DrawableObject o) {
    ...
    o.print()
    ...
  }

We would like to use this function with objects of classes derived from Draw able Object:

  Circle acircle
  Point apoint
  Rectangle arectangle
 
  display(apoint)      /* Should invoke apoint.print() */
  display(acircle)     /* Should invoke acircle.print() */
  display(arectangle)  /* Should invoke arectangle.print() */

The actual method should be defined by the content of the object o of function display(). Since this is somewhat complicated, here is a more abstract example:

  class Base {
  attributes:
 
  methods:
    virtual foo()
    bar()
  }
 
  class Derived inherits from Base {
  attributes:
 
  methods:
    virtual foo()
    bar()
  }
 
  demo(Base o) {
    o.foo()
    o.bar()
  }
 
  Base abase
  Derived aderived
 
  demo(abase)
  demo(aderived)

In this example we define two classes Base and Derived. Each class defines two methods foo() and bar(). The first method is defined as virtual. This means that if this method is invoked its definition should be evaluated by the content of the object.

We then define a function demo() which takes a Base object as its argument. Consequently, we can use this function with objects of class Derived as there is-a relation holds. We call this function with a Base object and a Derived object, respectively.

Suppose, that foo() and bar() are defined to just print out their name and the class in which they are defined. Then the output is as follows:

  foo() of Base called.
  bar() of Base called.
  foo() of Derived called.
  bar() of Base called.

Why is this so? Let's see what happens. The first call to demo() uses a Base object. Thus, the function's argument is filled with an object of class Base. When it is time to invoke method foo() it's actual functionality is chosen based on the current content of the corresponding object o. This time, it is a Base object. Consequently, foo() as defined in class Base is called.

The call to bar() is not subject to this content resolution. It is not marked as virtual. Consequently, bar() is called in the scope of class Base.

The second call to demo() takes a Derived object as its argument. Thus, the argument o is filled with a Derived object. However, o itself just represents the Base part of the provided object derived.

Now, the call to foo() is evaluated by examining the content of o, hence, it is called within the scope of Derived. On the other hand, bar() is still evaluated within the scope of Base.

Objects of super classes can be filled with objects of their subclasses. Operators and methods of subclasses can be defined to be evaluated in two contexts:

1. Based on object type, leading to an evaluation within the scope of the super class.

2. Based on object content, leading to an evaluation within the scope of the contained subclass.

The second type is called polymorphism.

In a word, Polymorphism means the sending of a message to an object without concern about how the software is going to accomplish the task, and furthermore it means that the task can be executed in completely different ways depending on the object that receives the message (in C++, polymorphism is implemented through the use of virtual functions). When the decision as to which actions are going to be executed is made at run-time, the polymorphism is referred to as late binding (as in the case of virtual functions). If they are made at compile time then it is known as early binding.

Page-6

<- Previous Page Next Page ->