Text Box: Object Oriented Programming

Set V


Polymorphism
And
Static & Dynamic Binding

S. Y. B.Sc. ( IT ) 

 III Semester

 

 


Polymorphism

 

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. Here, the type of a variable depends on its content. Thus, its type depends on the content at a specific time:

  v := 123        /* v is integer */

  ...             /* use v as integer */

  v := 'abc'      /* v "switches" to string */

  ...             /* use v as string */

 

Definition (Polymorphism (1)) 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, suppose you want to define a function isNull() 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 isNull. In programming languages without polymorphism for functions we cannot declare these two functions: The name isNull would be doubly defined. 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 isNull 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) */

 

 

 

 

 

Definition (Polymorphism (2)) 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 the is-a relation holds.

 

Now consider a function display() which should be used to display drawable 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 DrawableObject:

  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 Derive. 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 the 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 aderived.

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.

 

Definition (Polymorphism (3)) Objects of superclasses can be filled with objects of their subclasses. Operators and methods of subclasses can be defined to be evaluated in two contextes:

  1. Based on object type, leading to an evaluation within the scope of the superclass.
  2. Based on object content, leading to an evaluation within the scope of the contained subclass.
  3. The second type is called polymorphism.

 

 

Static and Dynamic Binding

In strongly typed programming languages you typically have to declare variables prior to their use. This also implies the variable's definition where the compiler reserves space for the variable. For example, in Pascal an expression like

 

  var i : integer;

 

declares variable i to be of type integer. Additionally, it defines enough memory space to hold an integer value.

 

With the declaration we bind the name i to the type integer. This binding is true within the scope in which i is declared. This enables the compiler to check at compilation time for type consistency. For example, the following assignment will result in a type mismatch error when you try to compile it:

 

  var i : integer;

  ...

  i := 'string';

 

We call this particular type of binding ``static'' because it is fixed at compile time.

 

Definition (Static Binding) If the type T of a variable is explicitly associated with its name N by declaration we say that N is statically bound to T. The association process is called static binding.

 

There exist programming languages which are not using explicitly typed variables. For example, some languages allow to introduce variables once they are needed:

 

  ...       /* No appearance of i */

  i := 123  /* Creation of i as an integer */

 

The type of i is known as soon as its value is set. In this case, i is of type integer since we have assigned a whole number to it. Thus, because the content of i is a whole number, the type of i is integer.

 

Definition (Dynamic Binding) If the type T of a variable with name N is implicitly associated by its content we say that N is dynamically bound to T. The association process is called dynamic binding.

 

Both bindings differ in the time when the type is bound to the variable. Consider the following example which is only possible with dynamic binding:

 

  if somecondition() == TRUE then

    n := 123

  else

    n := 'abc'

  endif

 

The type of n after the if statement depends on the evaluation of somecondition(). If it is TRUE, n is of type integer whereas in the other case it is of type string.

 


 


Exception Handling

( When the contract is broken )

 

Exceptions have two different definitions:

 

·        an event that causes suspension of normal application execution, and

·        a set of information directly relating to the event that caused suspension of normal application execution.

 

Exceptions can be contrasted with an older, less reliable technology: “error codes.” The idea behind error codes was fairly simple. You would request that an application, or part of an application, accomplish some work. One of the pieces of information that would be returned to the requester would be an error code. If all had gone well, the error code would typically have a value of zero. If any problems had occurred, the error code would have a non-zero value. It was also quite common to associate different non-zero values of an error code with specific errors.

Error codes suffered from two major problems:

 

·        No one was forced to actually check the value of returned error codes.

·        Changes (additions, deletions, and modifications) in the meanings of the special values assigned to error codes were not automatically passed on to interested parties. Tracking the effects of a changed error code value often consumed a significant amount of resources.

 

To understand how exceptions directly address both of these issues, we first need to understand how exceptions typically work:

 

Ø      Exceptions may be defined by the environment or by the user.

Ø      When an exceptional (but not unforeseen) condition occurs, an appropriate exception is activated. (People use different terms to express the activation of an exception. The most common is “raise.” Less commonly, people use the terms “throw” or “activate.”) This activation may be automatic (controlled by the environment) or may be expressly requested by the designer of the object or application.

 

Examples of exceptional conditions include trying to remove something from an empty container, directing an elevator on the top floor to "go up," and attempting to cause a date to take on an invalid value like "February 31, 1993."

Ø      Once the exception is activated, normal application execution stops and control is transferred to a locally defined exception handler, if one is present. If no locally defined exception handler is present or if the exception handler is not equipped to handle the exception, the exception is propagated to the next higher level of the application. Exceptions cannot be ignored. An exception will continue to be sent to higher levels of the application until it is either turned off or the application ceases to function.

Ø      An exception handler checks to see what type of exception has been activated. If the exception is one that the handler recognizes, a specific set of actions is taken. Executing a set of actions in response to an exception is known as “handling the exception.” Handling an exception deactivates the exception; the exception will not be propagated any further.

 

Unlike error codes, exceptions cannot be ignored. Once an exception has been activated, it demands attention. In object-oriented systems, exceptions are placed in the public interfaces of objects. Changes in the public interfaces of objects very often require an automatic rechecking of all other objects that invoke operations in the changed objects. Thus, changes in exceptions result in at least a partially automated propagation of change information.