OVERRIDING AND POLYMORPHISM


CONTENTS

1. Overriding
1.1. Overriding and the super keyword
2. Use of Private, Public and Protected Modifiers with Overriding
3. Polymorphism



1. OVERRIDING

Given a class hierarchy, a sub-class will inherit the methods in the super-class. However, if the sub-class includes a method with the same "signature" as a method in the super-class the super-class will not be inherited. We say that the sub-class method overrides the super-class method. Note that overriding should not be confused with overloading. Overloading involves providing several methods with the same name, but having different parameter lists; overriding involves providing several methods with the same name and the same parameter list, but declared in classes which are in a sub-class/super-class relationship.

If we consider the class given in Table 1 (ClassOne), this contains two methods:

  1. function1 which returns its argument divided by 2, and
  2. function2 which returns the result of a call to function1 raised to the power 2.
// Overriding Example Classes
// Frans Coenen
// 9 March 2000
// Dept Computer Science, University of Liverpool

class ClassOne {
		
    // ---------- METHODS ----------

    /* Function 1 */

    public float function1(float num) {
        return(num/2);
	}

    /* Function 2 */

    public float function2(float num) {
        return((float) Math.pow(function1(num),2));
	}	
    }

Table 1: ClassOne

The class given in Table 2 (ClassTwo) is a subclass of ClassOne (i.e. it extends ClassOne) which has its own function1 method that returns its argument divided by 3. Note that this method has a signature identical to the method of the same name in the super class, therefore we say that the function1 method in ClassTwo overrides the function1 method in ClassOne (not overloads).

// Overriding Example Classes - ClassTwo
// Frans Coenen
// 9 March 2000
// Dept Computer Science, University of Liverpool

class ClassTwo extends ClassOne {
		
    // ---------- METHODS ----------

    /* Function 1 */

    public float function1(float num) {
        return(num/3);
	}
    }

Table 2: ClassTwo (subclass of ClassOne)

The relationship between these two classes is illustrated in Figure 1.

In Table 3 an application class that interacts with the above class hierarchy is presented. In this code two instances are created:

  1. object1 an instance of ClassOne, and
  2. object2 an instance of ClassTwo.
OVERRIDING CLASS DIAGRAM

Figure 1: class hierarchy diagram showing relationship between ClassOne and ClassTwo classes.

The function1 method is then called with respect to both instances, object1 and object2, in turn and then the function2 method is called in a similar manner. The question is the:

Which function1 method, that defined in ClassOne or ClassTwo, will be invoked when we call function2 with a ClassTwo instance?

At first glance it might be expected that the ClassOne version of function1 will be called because function2 is defined in ClassOne; but this is not the case, of course, because function2 is inherited by instances of ClassTwo and thus is effectively is an instance method of this sub-class. Thus the ClassTwo version of the function1 method will be invoked when function2 is called with an instance of ClassTwo, and the ClassOne version when function2 is called with an instance of ClassOne. This is illustrated by the results presented in Table 4.

// Overriding Example application
// Frans Coenen
// 9 March 2000
// Dept Computer Science, University of Liverpool

class OverridingApp {
	
    // ----------- METHODS ----------
    
    /* Main method */
    
    public static void main(String[] args) {
        
	// Create instance of ClassOne and ClassTwo
	
	ClassOne object1 = new ClassOne();
	ClassTwo object2 = new ClassTwo();
	
	// Output
	
	System.out.println("object1.function1(12) = " +
	    		object1.function1(12));
	System.out.println("object2.function1(12) = " +
			object2.function1(12));
	System.out.println("object1.function2(12) = " +
			object1.function2(12));
	System.out.println("object2.function2(12) = " +
			object2.function2(12));
	}
    }

Table 3: Application class

$ java OverridingApp
object1.function1(12) = 6.0
object2.function1(12) = 4.0
object1.function2(12) = 36.0
object2.function2(12) = 16.0  

Table 4: Sample output from application class presented in Table 3


1.1. Overriding and The Use of The super Keyword

If, in the above case, we wanted to always invoke the function1 method defined in the ClassOne class when calling function2, while at the same time maintaining the overriden version of this function in the ClassTwo class; we would have to write a ClassTwo method that overrides the ClassOne function2 method and which insists on the super class version of function1. To do this we must prefix the method call with the keyword super to indicate that we wish to invoke the super class version of this method. A version of the ClassTwo class definition that does this is presented in Table 5 together with some sample output in (Table 6).

// Overriding Example Classes - ClassTwo
// Frans Coenen
// 9 March 2000
// Dept Computer Science, University of Liverpool
    
class ClassTwo extends ClassOne {
		
    // ---------- METHODS ----------
    
    /* Function 1 */
    
    public float function1(float num) {
        return(num/3);
	}
    
    /* Function 2 */
    
    public float function2(float num) {
        return((float) Math.pow(super.function1(num),2));
	}    
    }

Table 5: Second version of ClassTwo class

$ java OverridingApp
object1.function1(12) = 6.0
object2.function1(12) = 4.0
object1.function2(12) = 36.0
object2.function2(12) = 36.0  

Table 6: Sample output




2. USE OF PUBLIC, PRIVATE AND PROTECTED MODIFIERS WITH OVERRIDING

The code in Table 7 presents two classes such that the seconf class has a method, function2, which overrides the method of the same name in the parent class.

// Overriding Example Classes
// Frans Coenen
// 9 March 2000
// Dept Computer Science, University of Liverpool

class ClassOne {    
	 
    // ----------- METHODS ---------- 
    
    /* Function 1 */
    
    public void function1(int num) {
        System.out.println("num = " + num);
        function2(num);
	}

    /* Function 2 */

    private void function2(int num) {
        System.out.println("num * 2 = " + (num*2));
        }       
    }
    
// Overriding Example Classes - ClassTwo
// Frans Coenen
// 9 March 2000
// Dept Computer Science, University of Liverpool
    
class ClassTwo extends ClassOne {
		
    // ----------- METHODS ----------   
    
    /* Function 2 */
    
    private void function2(int num) {
        System.out.println("num * 3 = " + (num*3));
        }
    }

Table 7: Example classes

Table 8 gives an application class to be used in conjunction with the classes presented in Table 7 together with some sample output.

// Overriding Example application
// Frans Coenen
// 9 March 2000
// Dept Computer Science, University of Liverpool

class OverridingApp {
	
    // ------------------ METHODS ------------------------ 
    
    /* Main method */
    
    public static void main(String[] args) {
        
	// Create instance of ClassOne and ClassTwo
	
	ClassOne object1 = new ClassOne();
	ClassTwo object2 = new ClassTwo();
	
	// Output
	
	System.out.println("object1.function1(12) = ");
	object1.function1(12);
        System.out.println("object2.function1(12) = ");
	object2.function1(12);
        }
    }

Table 8: Applicatuion class for use with classes presented in Table 7

$ java OverridingApp
object1.function1(12) =
num = 12
num * 2 = 24
object2.function1(12) =
num = 12
num * 2 = 24

Table 9: Sample output generated form application class presented in Table 8

From the output presented in Table 9 there is clearly something wrong. When we call function1 with an instance of ClassTwo the function2 method associated with the ClassOne class is called! To make sure that the function2 method associated with the ClassTwo class is called we mist modify the two function2 methods so that they are both "protected". This is illustrated in Table 10. Some associated output is presented in Table 11.

// Overriding Example Classes
// Frans Coenen
// 9 March 2000
// Dept Computer Science, University of Liverpool

class ClassOne {    
	 
    // ----------- METHODS ---------- 
    
    /* Function 1 */
    
    public void function1(int num) {
        System.out.println("num = " + num);
        function2(num);
	}

    /* Function 2 */

    protected void function2(int num) {
        System.out.println("num * 2 = " + (num*2));
        }       
    }
    
// Overriding Example Classes - ClassTwo
// Frans Coenen
// 9 March 2000
// Dept Computer Science, University of Liverpool
    
class ClassTwo extends ClassOne {
		
    // ----------- METHODS ----------   
    
    /* Function 2 */
    
    protected void function2(int num) {
        System.out.println("num * 3 = " + (num*3));
        }
    }

Table 10: Revised classes

$ java OverridingApp
object1.function1(12) =
num = 12
num * 2 = 24
object2.function1(12) =
num = 12
num * 3 = 36

Table 11: New smaple output




3. POLYMORPHISM

In Table 12 the same code given in Table 1 is presented except that the function1 method is now a class method with the calling object passed in as the second argument. Which function1 method is called now? The answer is that it is still depends on the class of the object passed in, if this is an instance of the ClassTwo method the ClassTwo version of function1 will be called, and if it is an instance of the ClassOne method the ClassOne version of function1 will be called (i.e. the result presented in Table 4). Table 13 show the necessary changes that need to be made to the application calls given the revised version of ClassOne presented in Table 12.

// Overriding Example Classes
// Frans Coenen
// 9 March 2000
// Dept Computer Science, University of Liverpool

class ClassOne {    
		
    // ---------- METHODS ---------- 
    
    /* Function 1 */
    
    public float function1(float num) {
        return(num/2);
	}
    
    /* Function 2 */
    
    static float function2(float num, ClassOne object) {
        return((float) Math.pow(object.function1(num),2));
	}	
    }

Table 12: Second version of ClassOne class (featuring polymorphism)

// Overriding Example application
// Frans Coenen
// 9 March 2000
// Dept Computer Science, University of Liverpool

class OverridingApp {
	
    // ----------- METHODS ----------
    
    /* Main method */
    
    public static void main(String[] args) {
        
	// Create instance of ClassOne and ClassTwo
	
	ClassOne object1 = new ClassOne();
	ClassTwo object2 = new ClassTwo();
	
	// Output
	
	System.out.println("object1.function1(12) = " +
				object1.function1(12));
	System.out.println("object2.function1(12) = " +
				object2.function1(12));
	System.out.println("ClassOne.function2(12,object1) = " +
				ClassOne.function2(12,object1));
	System.out.println("ClassOne.function2(12,object2) = " +
				ClassOne.function2(12,object2));
	}
    }

Table 13: Revised version of application class presented in Table 3

How does the compiler know whether the function2 method should be called with the ClassTwo function1 method or the ClassOne function1? The answer is that it does not know. The decision as to which function1 is to be used is left till run time using a technique called Dynamic method lookup. This is a technique whereby each object has a table of its methods, java then searches through this table for the correct version of any overriden methods at run time --- a feature known as polymorphism.

Polymorphism is thus a way of giving a method one name that is shared up and down a class hierarchy, with each class in the hierarchy implementing the method in a way appropriate to itself. Polymorphism only applies to specific sets of methods. To write a polymorphic class two things are required:

Note that although dynamic method lookup provides greater flexibility (e.g. it allows more extensive overriding of methods) there is an execution overhead associated with its use. Note also that dynamic method lookup is not required for static or private methods as these must be called in relation to a class or an instance respectively. Similar it is not required for methods (and classes) declared as final --- a final method cannot be overridden and a final class cannot be extended.




Created and maintained by Frans Coenen. Last updated 28 August 2002