Home | | Web Programming | Dynamic Method Dispatch - Java

Chapter: Java The Complete Reference : The Java Language : Inheritance

Dynamic Method Dispatch - Java

While the examples in the preceding section demonstrate the mechanics of method overriding, they do not show its power.

Dynamic Method Dispatch

 

While the examples in the preceding section demonstrate the mechanics of method overriding, they do not show its power. Indeed, if there were nothing more to method overriding than a name space convention, then it would be, at best, an interesting curiosity, but of little real value. However, this is not the case. Method overriding forms the basis for one of Java’s most powerful concepts: dynamic method dispatch. Dynamic method dispatch is the mechanism by which a call to an overridden method is resolved at run time, rather than compile time. Dynamic method dispatch is important because this is how Java implements run-time polymorphism.

 

Let’s begin by restating an important principle: a superclass reference variable can refer to a subclass object. Java uses this fact to resolve calls to overridden methods at run time. Here is how. When an overridden method is called through a superclass reference, Java determines which version of that method to execute based upon the type of the object being referred to at the time the call occurs. Thus, this determination is made at run time. When different types of objects are referred to, different versions of an overridden method will be called. In other words, it is the type of the object being referred to (not the type of the reference variable) that determines which version of an overridden method will be executed. Therefore, if a superclass contains a method that is overridden by a subclass, then when different types of objects are referred to through a superclass reference variable, different versions of the method are executed.

 

Here is an example that illustrates dynamic method dispatch:

 

// Dynamic Method Dispatch 


class A {

 

void callme() {

 

System.out.println("Inside A's callme method");

 

}

 

}

 

class B extends A { // override callme() 

void callme() {

System.out.println("Inside B's callme method");

 

}

 

}

 

class C extends A { // override callme() 

void callme() {

System.out.println("Inside C's callme method");

 

}

 

}

 

class Dispatch {

 

public static void main(String args[]) { 

A a = new A(); // object of type A

B b = new B(); // object of type B 

C c = new C(); // object of type C

A r; // obtain a reference of type A

 

r = a; // r refers to an A object 

r.callme(); // calls A's version of callme

 

r = b; // r refers to a B object 

r.callme(); // calls B's version of callme

 

r = c; // r refers to a C object 

r.callme(); // calls C's version of callme

 

}

 

}

The output from the program is shown here:

 

Inside A's callme method

 

Inside B's callme method

 

Inside C's callme method

 

This program creates one superclass called A and two subclasses of it, called B and C. Subclasses B and C override callme( )declared in A. Inside the main( ) method, objects of type AB, and C are declared. Also, a reference of type A, called r, is declared. The program then in turn assigns a reference to each type of object to r and uses that reference to invoke callme( ). As the output shows, the version of callme( ) executed is determined by the type of object being referred to at the time of the call. Had it been determined by the type of the reference variable, r, you would see three calls to A’s callme( ) method.

 

Why Overridden Methods?

 

As stated earlier, overridden methods allow Java to support run-time polymorphism. Polymorphism is essential to object-oriented programming for one reason: it allows a general class to specify methods that will be common to all of its derivatives, while allowing subclasses to define the specific implementation of some or all of those methods. Overridden methods are another way that Java implements the “one interface, multiple methods” aspect of polymorphism.

 

Part of the key to successfully applying polymorphism is understanding that the superclasses and subclasses form a hierarchy which moves from lesser to greater specialization. Used correctly, the superclass provides all elements that a subclass can use directly. It also defines those methods that the derived class must implement on its own. This allows the subclass the flexibility to define its own methods, yet still enforces a consistent interface. Thus, by combining inheritance with overridden methods, a superclass can define the general form of the methods that will be used by all of its subclasses.

Dynamic, run-time polymorphism is one of the most powerful mechanisms that object-oriented design brings to bear on code reuse and robustness. The ability of existing code libraries to call methods on instances of new classes without recompiling while maintaining a clean abstract interface is a profoundly powerful tool.

Applying Method Overriding

 

Let’s look at a more practical example that uses method overriding. The following program creates a superclass called Figure that stores the dimensions of a two-dimensional object. It also defines a method called area( ) that computes the area of an object. The program derives two subclasses from Figure. The first is Rectangle and the second is Triangle. Each of these subclasses overridesarea( ) so that it returns the area of a rectangle and a triangle, respectively.

 

 

// Using run-time polymorphism. 


class Figure {

 

double dim1; double dim2;

 

Figure(double a, double b) { dim1 = a;

 

dim2 = b;

 

}

 

double area() {

 

System.out.println("Area for Figure is undefined."); return 0;

 

}

 

}

 

class Rectangle extends Figure { Rectangle(double a, double b) {

super(a, b);

 

}

 

// override area for rectangle 


double area() {

 

System.out.println("Inside Area for Rectangle."); return dim1 * dim2;

 

}

 

}

 

class Triangle extends Figure { Triangle(double a, double b) {

super(a, b);

 

}

 

// override area for right triangle 

double area() {

 

System.out.println("Inside Area for Triangle."); return dim1 * dim2 / 2;

 

}

 

}

 

class FindAreas {

 

public static void main(String args[]) { Figure f = new Figure(10, 10); Rectangle r = new Rectangle(9, 5);

Triangle t = new Triangle(10, 8);

 

Figure figref;

 

figref = r;

 

System.out.println("Area is " + figref.area());

 

figref = t;

 

System.out.println("Area is " + figref.area());

 

figref = f;

 

System.out.println("Area is " + figref.area());

 

}

 

}

 

The output from the program is shown here:

 

Inside Area for Rectangle.

 

Area is 45

 

Inside Area for Triangle.

 

Area is 40

 

Area for Figure is undefined.

 

Area is 0

 

Through the dual mechanisms of inheritance and run-time polymorphism, it is possible to define one consistent interface that is used by several different, yet related, types of objects. In this case, if an object is derived from Figure, then its area can be obtained by calling area( ). The interface to this operation is the same no matter what type of figure is being used.


Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail
Java The Complete Reference : The Java Language : Inheritance : Dynamic Method Dispatch - Java |


Privacy Policy, Terms and Conditions, DMCA Policy and Compliant

Copyright © 2018-2024 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.