INTRODUCTION TO PROGRAMMING IN JAVA: INHERITANCE AND CLASS HIERARCHIES

(Plus abstract classes and methods, and interfaces)

NOTE: This set of www pages is not the set of www pages for the curent version of COMP101. The pages are from a previous version that, at the request of students, I have kept on line.


CONTENTS

1. Overview
2. Creating a class hierarchy in Java
2.1. The keyword super
3. Example problem - cylinder calculation
3.1. Requirements
3.2. Analysis
3.3. Design
 
3.4. Implementation
3.5. Testing
4. Overriding class methods
5. Abstract classes
5.1. Abstract methods
5.2. Abstract classes
6. Interfaces

Example includes a method that returns an instance.




1. OVERVIEW

The concept of inheritance was introduced earlier in this sequence of WWW pages. Inheritance allows classes to "inherit" class members from other classes. The advantages are:

Reuse of classes that have already been written (enhances software development efficiency).
Grouping of related classes thus allowing them to be declared more succinctly by avoiding cluttering of programs through rewriting of identical sections of code.
Enhanced understandability and readability by allowing classes to be arranged in a logical manner (especially in the case of large programs).

Some definitions (Figure 1):

  1. Using inheritance, classes can be arranged into a class hierarchy where classes declared lower down in the hierarchy inherit members from classes declared higher up in the hierarchy (the most general class will thus be found at the top of the hierarchy).
 
  1. The top most class is referred to as the base class or Fundamental Data Unit (FDU).
  2. The remaining classes are referred to as sub classes or derived classes.
  3. The classes from which a sub class is derived are referred to as its super classes (which will of course include the base class). Derived classes inherit attributes and operations from super classes, however a derived class may include new attributes and operations not included in a super class, or redefine super class operations.
INHERITANCE

Figure 1: Inheritance





2. CREATING A CLASS HIERARCHY IN JAVA

In all object oriented programming languages, when creating a sub-class we must somehow indicate to the compiler the super-class from which the sub class is derived. In Java this is done using the keyword extends. Consider the example code fragment given in Table 1. Here we have created a class called SuperClass and a sub-class of this class called SubClass. We have indicated to the Java compiler that SubClass is derived from the class SuperClass using the extends keyword:

class SubClass extends SuperClass {
   ...
   }

A class diagram for the code is given in Figure 2. Notice that the super class has a data member which is defined as being protected. The reason for this is that:

  • Although subClass inherits the data member dataItem1, from superClass; if dataItem1 was a private member of superClass it could only be accessed by a public method, contained in superClass, which can thus be called from subClass.
  • A method of this kind must be an instance method to access an instance field, and thus can only be invoked from outside the class by linking it, using the dot operator, to an instance of SuperClass or SubClass.

To avoid the need for this additional effort fields that are to be inherited by sub-classes as defined as being protected. Thus:

 
Private members --- Can only be accessed from within the class they are declared in.
Protected members --- Can only be accessed from within the class they are declared in and sub-classes there of.
Public members --- Can be accessed from anywhere.
SUPERCLASS CLASS DIAGRAM

Figure 2: SperClass Class Diagram

Therefore in Figure 2 the SuperClass class has one protected instance data member (dataItem1), a constructor, and a public instance method (outputData). The data member is inherited by the sub-class (the constructor and methods are public and so accessible from anywhere including the sub class). The SubClass class has a private data member (dataItem12), a constructor, and an instance method (outputDataItems). Instances of the class superClass therefore have one data member, while instances of the class subClass have two data members (one inherited).

// SUPER CLASS
// Frans Coenen
// Created Tuesday 16 March 1999
// Revised Monday 15 July 2002
// The University of Liverpool, UK   

class SuperClass {

    // ------------------- FIELDS --------------------- 
    
    protected int dataItem1;

    // ----------------- CONSTRUCTORS -----------------
    
    /* Constructor */
    
    public SuperClass(int input) {
    	dataItem1 = input;
	}
	
    // ------------------ METHODS ---------------------
    
    /* Ouput data */
    
    public void outputData() {
        System.out.println("OUTPUT ONE DATA ITEM");
	System.out.println("\tdataItem1 = " + dataItem1);
	}
    }
    
/****************************************************/
/*                                                  */
/*                      SUB CLASS                   */
/*                                                  */
/****************************************************/

class SubClass extends SuperClass {

    // ------------------- FIELDS --------------------- 

    private int dataItem2 = 2;
	

    // ----------------- CONSTRUCTORS ----------------- 
    
    public SubClass(int input1, int input2) {
    	super(input1);
	dataItem2 = input2;
	}
	
    // ------------------ METHODS ---------------------
    
    /* Output dataItem1 and dataItem2 */
       
    public void outputDataItems() {
        System.out.println("OUTPUT TWO DATA ITEMS");
    	System.out.println("\tdataItem1 = " + dataItem1);
	System.out.println("\tdataItem2 = " + dataItem2);
	}
    }

Table 1: Class hierarchy definition (SuperClass and SubClass)

// INHERITANCE EXAMPLE APPLICATION
// Frans Coenen
// Created Monday 15 July 2002
// The University of Liverpool, UK   

class ExampleApp {

    // ------------------- FIELDS --------------------- 
    
    /* None */

    // ----------------- CONSTRUCTORS -----------------
    
    /* None */
	
    // ------------------ METHODS ---------------------
	
    /* Main method */

    public static void main(String[] args) {
        
	// Create instances
	
    	SuperClass instance1 = new SuperClass(1);
	SubClass   instance2 = new SubClass(2,3);
	
	
        System.out.println("INSTANCE 1\n==========");
	instance1.outputData();
	
        System.out.println("INSTANCE 2\n==========");
	instance2.outputData();
	instance2.outputDataItems(); 
        }
    }

Table 2: Example application class

$ java ExampleApp
INSTANCE 1
==========
OUTPUT ONE DATA ITEM
        dataItem1 = 1
INSTANCE 2
==========
OUTPUT ONE DATA ITEM
        dataItem1 = 2
OUTPUT TWO DATA ITEMS
        dataItem1 = 2
        dataItem2 = 3

Table 3: Example output generated from code presented in Tables 1 and 2.


2.1 The Keyword super

If we wish to create an instance of a sub-class we (obviously) must use the constructor for this class. When invoked Java will also call the constructors for the super-classes starting with the base-class constructor and working down to the sub-class in question. This works fine provided that we only wish to invoke zero-argument (default) constructors. However, in the above example the SuperClass constructor has an argument! The question is then:

"how do we get the required argument to this constructor?"

The answer is that we use the super keyword as illustrated in the above code. The key word super thus allows us to make use of the constructor defined in the super class, and thus (in the above case) we can cause a value to be assigned to the dataItem1 data member defined in the super-class.

Note: when using the reserved word super it must always be the first statement in the constructor body.



3. EXAMPLE PROBLEM --- CYLINDER CALCULATION

Extends the circle class created previously.


3.1 Requirements

To produce a program that calculates the surface area and volume of a cylinder given its diameter and height (Figure 3). Assume that the diameter and height are input by the user as double precision floating point numbers. Area of a cylinder = (2 x AreaOfEnd) + (AreaOfSide) = (2 x PI x (diameter/2)^2) + (height x PI x diameter), Volume of a cylinder = AreaOfEnd x height = (PI x (diameter/2)^2) x height.

CYLINDER GEOMETRY

Figure 3: Cylinder geometry

CYLINDER CLASS DIAGRAM

Figure 4: Cylinder class diagram


3.2 Analysis

A class diagram for the proposed solution is given in Figure 4. Note that: (1) we have "extended" the circle class introduced previously by adding a subclass cylinder; and (2) inheritance is indicated by an "open" arrow, and aggregation by a "closed" arrow (remember that inheritance and aggregation are two very different things).


3.3 Design

From Figure 3 the design comprises three programmer defined classes; the existing Circle class, plus new Cylinder and CylinderApp classes.


3.3.1 Cylinder Sub-Class

Field Summary
private double cylinderHeight
           An instance field to store the height of an instance of the class Cylinder.
private double cylinderCircum
           An instance field to store the circumference of an instance of the class Cylinder.
private double cylinderArea
           An instance field to store surface area of an instance of the class Cylinder.

Constructor Summary
Cylinder(double newRad, double newHeight)
           Constructs an instance of the class Cylinder given a cylinder height and radius as formal parameters (newRad and newHeight) which are assigned to the instance variables circleRadius (in the parent Circle class) and cylinderHeight respectively.

Method Summary
public void calcCylinderVolume()
           Public instance method to determine volume of a given cylinder. Proceed by first calculating the area of the circle end of the cylinder using the instance method defined in the existing Circle class. (This data may already be available but we can not guarantee this.) Then calculate the volume using the given identity.
public void calcCylinderArea()
           Public instance method to calculate the surface area of a given cylinder. Again we proceed by calculating the area of the circle ends of the cylinder (despite the fact that this may have been calculated previously because we can not guarantee this), and then determine the area using the given identity.
public double getCylinderVolume()
           Public instance method to return volume of cylinder (a private data member).
public double getCylinderArea()
           Public method similar to getCylinderVolume method (see above) to return the surface area of the cylinder (a private data member).

Nassi-Shneiderman charts for all the above methods are presented in Figure 5.

NASSI-SHNEIDERMAN CHART FOR CYLINDER CLASS

Figure 5: Nassi-Shneiderman charts for Cylinder class methods


3.3.2 CylinderApp Class

Field Summary
private static Scanner input
           A class instance to facilitate input from the input stream.

Method Summary
public static void main(String[] args)
           Main method. Calls the createCylinder class method (see below) to create and instantiate an instance of the class Cylinder. Then calls calcCylinderArea and calcCylinderVolume instance methods (defined in Cylinder sub-class of the class Circle) to calculate the surface area and volume of the cylinder respectively. Finally output the result using the getCylinderVolume and getCylinderArea instance methods to access the appropriate data.
private static Cylinder createCylinderObj()
           Class method to allow user to input a cylinder radius and height and then create an instance of the type Cylinder with the given data using the constructor defined in the Cylinder sub-class of the class Circle.

Nassi-Shneiderman charts for the above methods are presented in Figure 5.

NASSI-SHNEIDERMAN CHART FOR CYLINDER APPLICATION CLASS

Fig 5: Nassi-Shneiderman charts for CylinderApp class methods


3.4. Implementation


3.4.2 Cylinder class

We will add this to the source file that contains the circle class. We can do this becuase cylinder is a sub-class of circle.

// CIRCLE CLASS
// Frans Coenen
// Created Tuesday 2 March 1999
// Extended Tuesday 16 March 1999
// The University of Liverpool, UK
	
/************************************************/
/*                                              */
/*                CIRCLE CLASS                  */
/*                                              */
/************************************************/

class Circle {

    // ------------------- FIELDS ------------------------               
        
    protected double circleRadius;
    protected double circleCircum;
    protected double circleArea;
    
    // ------------------ CONSTRUCTORS -------------------
    
    /* Circle constructor */
    
    public Circle(double newRadius) {
    	circleRadius = newRadius;
	}
	
    // ------------------ METHODS ------------------------
    
    /* Calculate circumference */
    
    public void calcCircleCircum() {
    	circleCircum = 2.0f*Math.PI*circleRadius;
    	}
	
    /* Calculate area */
    
    public void calcCircleArea() {
    	circleArea = Math.PI*circleRadius*circleRadius;
	}
    	
    /* Output circleCircum and area */

    public String toString() {
    	return("Circumference = " + circleCircum +
	       "Area          = " + circleArea);
    	}
    }

/***********************************************/
/*                                              */
/*               CYLINDER CLASS                 */
/*                                              */
/***********************************************/

class Cylinder extends Circle {

    // ------------------- FIELDS ------------------------               
        
    private double cylinderHeight;
    private double cylinderVolume;
    private double cylinderArea;
    
    // ------------------ CONSTRUCTORS -------------------
    
    /* Cylinder constructor */
    
    public Cylinder(double newRad, double newHeight) {
    	super(newRad);
	cylinderHeight = newHeight;
	}
	
    // ------------------ METHODS ------------------------
    
    /* Calculate volume of cylinder */
    
    public void calcCylinderVolume() {
	calcCircleArea();
	cylinderVolume = circleArea*cylinderHeight;
    	}
    
    /* Calculate surface area of cylinder */
    
    public void calcCylinderArea() {
    	calcCircleArea();
	calcCircleCircum();
	cylinderArea = (2*circleArea)+(circleCircum*cylinderHeight);
    	}
    
    /* Return cylinder volume */
    
    public double getCylinderVolume() {
    	return(cylinderVolume);
 	}   

    /* Return cylinder area */
    
    public double getCylinderArea() {
    	return(cylinderArea);
 	}   
    }	

Table 4: Cylinder class hierarchy

Notes on the above:

We have included the modifier extends in the signature for Cylinder class to tell the Java compiler that this is a sub-class of the named class.
The Circle instance fields which were originally private members are now protected members of the class.
We use the super key word to access the Circle class constructor from the Cylinder class constructor.

3.4.3 CylinderApp class

// CYLINDER APPLICATION CLASS
// Frans Coenen
// Created Wednesday 17 March 1999
// Revised: Monday 4 July 2005
// The University of Liverpool, UK   

import java.util.*;

class CylinderApp {

    // ------------------- FIELDS ------------------------    
    
    // Create Scanner class instance

    private static Scanner input = new Scanner(System.in);
    
    // ------------------ METHODS ------------------------  
    
    /* Main methid */
    
   public static void main(String[] args) {
	
	// Create a cylinder object
	
	Cylinder cylinder1 = createCylinderObj();
	
	// Calculate area and volume
	
	cylinder1.calcCylinderArea();
	cylinder1.calcCylinderVolume();
	
	// Output result
	
	System.out.println("Volume       = " + 
				cylinder1.getCylinderVolume());
	System.out.println("Surface area = " + 
				cylinder1.getCylinderArea());
	}

    /* Create cylinder object */
    
    private static Cylinder createCylinderObj() {
	
	// Input radius and height
	
    	System.out.println("Input a radius and height (doubles): ");
	double radius = input.nextDouble(); 
	double height = input.nextDouble(); 
	
	// Create and return new Cylinder instance
	
	Cylinder newCylinder = new Cylinder(radius,height);
	return(newCylinder);
    	}
    }

Table 5: Cylinder application program


Note: The last two lines in the createCylinderObj method in Table 5:

Cylinder newCylinder = 
		new Cylinder(radius,height);
return(newCylinder);

Can be combined by embedding the constructor in the return statement:

return(new Cylinder(radius,height));

3.5. Testing

1. Arithmetic testing Test using all possible combinations of negative, positive and zero input values for the radius and height user inputs as shown in the given table (right). Note that the code allows input of negative numbers. This is likely to be undesirable, but at this stage we are still unable to address this!

TEST CASEEXPECTED RESULT
RadiusHeightAreaVolume
0.0 0.0 0.0 0.0
0.0 10.0 0.0 0.0
0.0-10.0 0.0 0.0
2.5 0.0 39.2699 0.0
2.5 10.0 196.3495 196.3495
2.5-10.0-117.8097-196.3495
-2.5 0.0 -39.2699 0.0
-2.5 10.0-117.25 196.3495
-2.5-10.0 196.3495-196.3495

2. Data validation testing Provide input data with incorrect syntax, both blatant and subtle. Suggested test cases given below. The '*' symbol indicates that we do not expect to input a second value as we expect the code to fail.

TEST CASEEXPECTED RESULT
RadiusHeightAreaVolume
x*java.lang.numberFormatException
2.e*java.lang.numberFormatException
2.5xjava.lang.numberFormatException
2.51e.0java.lang.numberFormatException



4. OVERRIDING CLASS METHODS

Given a class hierarchy, a sub-class will inherit the "protected" methods in the super-class. However, if the sub-class includes a protected method with the same "signature" (i.e. the same name and parameter list) 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 protected methods with the same name and the same parameter list, but declared in classes which are in a sub-class/super-class relationship.

Further details.




5. ABSTRACT METHODS AND CLASSES


5.1. Abstract Methods

An abstract method is a method defined by only its signature, i.e. it has no body. The implementational detail for the method, i.e. the body, is supplied by the sub-classes of the class in which the method is defined according to the nature of each sub-class. We indicate such a method to the compiler using the keyword abstract:

abstract public void function1(int);
:

An abstract method can be thought of as a "template" or "blueprint" for a method whose implementational details are contained in the sub-classes of the class in which the abstract method is defined.


5.1. Abstract Classes

An abstract class is one that is identified by the keyword abstract. An abstract class does not have to contain an abstract method, however, a class that does contain at least one abstract method is considered to be an abstract class and must therefore be identified using the keyword abstract (otherwise it will not compile). Similarly an abstract class can contain methods that are not abstract. A frame work for a Java abstract class is presented in Table 6.

abstract class ClassName {
    
    < FIELD DEFINITIONS >
    
    < METHOD DEFINITIONS >
    	
    }

Table 6: Framework for abstract class

It is not possible to create instances of an abstract class as there will be no means of implementing the abstract methods it may contain --- instead we extend the abstract class and provide the body for the abstract methods contained in the abstract class in this sub-class.

Further details.




6. INTERFACES

We have seen that Java does not specifically support multiple-inheritance, however something like it can be achieved using an interface. An interface is a class that must contain only abstract methods and/or constants. Thus the interface supplies a specification of methods which must be implemented by a sub-class of the interface, we say that the subclass implements the interface.

Thus we can define a class, SubClass, which extends a super class (SuperClass) while at the same time implementing (say) two interface calsses --- Interface1 and Interface2:

class SubClass extends SuperClass implements Interface1, Interface2 {
    
    < FIELD DEFINITIONS >
    
    < METHOD DEFINITIONS >
    	
    }  

Further details.




Created and maintained by Frans Coenen. Last updated 10 February 2015