INTRODUCTION TO PROGRAMMING IN JAVA: ARRAYS OF INSTANCES (PLUS NOTE ON AGGREGATION)

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. Introduction
2. Simple example
3. Example problem - Voyage disance
3.1. Requirements
 
3.2. Analysis
3.3. Design
3.4. Implementation
3.5. Testing
4. Note on aggregation (Composition)

First example includes a static array of instances, and the second a dynamic array of instances of the Point_2D defined previously (and which in turn creates and uses an instance of the class Triangle).




1. INTRODUCTION

The elements in an array do not have to be made up of primitive types, they can be compound types. For example the elements of an array may be other arrays or instances of a particular class. Here we will discuss arrays of instances (arrays of arrays are not part of the COMP101 syllabus).




A SIMPLE EXAMPLE

In an earlier example program fragment we presented a class Dec2Hex, which had one field, the constant MAX_NUM_DIGITS which was set to 3; and three methods associated with it (Figure 1) used to output numbers in hexadecimal format.

CLASS DIAGRAM FOR DECEOMAL TO HEXADECIMAL CONVERSION CLASS

Figure 1: Class diagram for Dec2Hex class

Suppose we now wish to create an array of instances of the class (type) Dec2Hex. We could do this as follows:

Dec2Hex [] hexArray = 
	new Dec2Hex[LENGTH]

Here we have declared an array of N elements (N = LENGTH) where each element is an instance of the class Dec2Hex. To initialize this array we must use a constructor thus:

for (index=LOWER_BOUND;index < LENGTH;
	    index++) 
    hexArray[index] = new 
            Dec2Hex( < SOME_VALUE > );

Note that this requires some modification of the Dec2Hex class so that it includes:

  1. A field, lets call it value, in which we can place the number which we wish to convert.
  2. An appropriate constructor to assign a value to this field.
  3. A second version of the convertDec2Hex method that makes use of the value field (instead of a decimal number supplied as an argument to the method). Remember that this is called overloading.

 

Any changes we make to the Dec2Hex class should be such that any application program that we might have written previously, using the class as was, still operates as expected. The previous version of the Dec2Hex class operated using the default constructior:

public Dec2Hex() {
    }

If we write our own constructor this will over-ride the default constructor thus, to ensure that any previous application program continues to operate, we must also include a default constructor of the above form in our revised Dec2Hex class.

The required modifications to the Dec2Hec class are presented in Table 1. A revised class diagram for the Dec2Hex class is given in Figure 2.

CLASS DIAGRAM FOR WEB COLOURS PROBLEM

Figure 2: Class diagram showing revisions to Dec2Hex class

The application Class (Dec2HexAppVer2) presented in Table 2 uses the above constructs to create an array of 256 instances of the class Hex2dex, instantiates the value attribute for each of these elements, converts each value to hex' format, and then outputs the entire array. The output is shown in Table 3.


// DECIMAL TO HEXADECIMAL COMVERSION 
// Frans Coenen
// Thursday 1 July 1999
// Revissions Wednesday 23 November 2002
// The University of Liverpool, UK   

class Dec2Hex {
    
    // ------------------ FIELDS ------------------------
     
    private int value; 
    private static final int MAX_NUM_DIGITS = 3;
       
    // ------------------ METHODS ------------------------  
    
    public Dec2Hex() {
        }
	
    public Dec2Hex(int number) {
        value = number;
	}
	
    /* ------ OUTPUT HEXADECIMAL NUMBER ------ */
    
    /* Output a hexadecimal number with leading zeroes. */
    
    public void convertDec2Hex(int count) {
        final int HEX_BASE = 16;
	       
        // Base case 

	if (value < HEX_BASE) {
		padWithZeros(count);
		outputHexDigit(value);
        	}
		
	// Recursive step
                   
        else {     
              convertDec2Hex(value/HEX_BASE,count=count+1);
              outputHexDigit(value%HEX_BASE);
              }   
        }
    
    public void convertDec2Hex(int number, int count) {
        final int HEX_BASE = 16;
	       
        // Base case 

	if (number < HEX_BASE) {
		padWithZeros(count);
		outputHexDigit(number);
        	}
		
	// Recursive step
                   
        else {     
              convertDec2Hex(number/HEX_BASE,count=count+1);
              outputHexDigit(number%HEX_BASE);
              }   
        }
	
< REST OF CODE SAME AS BEFORE >

Table 1: Modified Dec2Hex class

// DECIMAL TO HEXADECIMAL VERSION 2 APPLICATION 2
// Frans Coenen
// Thursday 12 July 1999
// The University of Liverpool, UK 

class Dec2HexAppVer2 {

    // ------------------ FIELDS ------------------------  
    
    static private final int LOWER_BOUND = 0;
    static private final int LENGTH = 256;
    static private final int NUMBERS_PER_LINE = 15;
    static private Dec2Hex [] hexArray = new Dec2Hex[LENGTH];
    
    // ------------------ METHODS ------------------------  
    
    /* Main */
    
    public static void main(String[] args) {
	int index;
	int newLineCounter=0;
        
	// Initialise Dec2Hex array of given size. 
	
	hexArray = new Dec2Hex[LENGTH];
	for (index=LOWER_BOUND;index < LENGTH;index++) 
	    hexArray[index] = new Dec2Hex(index);
	
	// Output
	
	for(index=LOWER_BOUND;index < LENGTH;index++) {
	    hexArray[index].convertDec2Hex(0); 
	    System.out.print(" ");
	    if (newLineCounter == NUMBERS_PER_LINE) {
	        System.out.print("\n");
	        newLineCounter=0;
		}
	    else newLineCounter++;	
	    }
	    
	// End
	
	System.out.println("\n");    
        }
    }

Table 2: Array of (Dec2Hex) objects example application class

0000 0001 0002 0003 0004 0005 0006 0007 0008 0009 000A 000B 000C 000D 000E
000F 0010 0011 0012 0013 0014 0015 0016 0017 0018 0019 001A 001B 001C 001D
001E 001F 0020 0021 0022 0023 0024 0025 0026 0027 0028 0029 002A 002B 002C
002D 002E 002F 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003A 003B
003C 003D 003E 003F 0040 0041 0042 0043 0044 0045 0046 0047 0048 0049 004A
004B 004C 004D 004E 004F 0050 0051 0052 0053 0054 0055 0056 0057 0058 0059
005A 005B 005C 005D 005E 005F 0060 0061 0062 0063 0064 0065 0066 0067 0068
0069 006A 006B 006C 006D 006E 006F 0070 0071 0072 0073 0074 0075 0076 0077
0078 0079 007A 007B 007C 007D 007E 007F 0080 0081 0082 0083 0084 0085 0086
0087 0088 0089 008A 008B 008C 008D 008E 008F 0090 0091 0092 0093 0094 0095
0096 0097 0098 0099 009A 009B 009C 009D 009E 009F 00A0 00A1 00A2 00A3 00A4
00A5 00A6 00A7 00A8 00A9 00AA 00AB 00AC 00AD 00AE 00AF 00B0 00B1 00B2 00B3
00B4 00B5 00B6 00B7 00B8 00B9 00BA 00BB 00BC 00BD 00BE 00BF 00C0 00C1 00C2
00C3 00C4 00C5 00C6 00C7 00C8 00C9 00CA 00CB 00CC 00CD 00CE 00CF 00D0 00D1
00D2 00D3 00D4 00D5 00D6 00D7 00D8 00D9 00DA 00DB 00DC 00DD 00DE 00DF 00E0
00E1 00E2 00E3 00E4 00E5 00E6 00E7 00E8 00E9 00EA 00EB 00EC 00ED 00EE 00EF
00F0 00F1 00F2 00F3 00F4 00F5 00F6 00F7 00F8 00F9 00FA 00FB 00FC 00FD 00FE
00FF    

Table 3: Output from application program given in Table 2



3. EXAMPLE PROBLEM - VOYAGE DISTANCE


3.1. Requirements

The passage plan of a ship is described in terms of a sequence of course alteration points known as way points (Figure 3). The total distance of a voyage is then described by the summation of the lengths connecting a sequence of such way points.

Develop a program that will allow a user to input a sequence of way points (described in terms of X and Y coordinates) and then calculate the overall distance. Note: make use of the point_2D class used in the Geographic Distance example problem.

VOYAGE

Figure 3: Passage plan


3.2. Analysis

We can generate the voyage distance by first allowing the user to specify the number of way points (there must be at least two) and then creating an array of instances of the class Point_2D (defined earlier and which in turn creates and uses an instance of the class Triangle) such that each instance of this array represents a single way point. Note that the array will be dynamic in the sense that we will not know at compile time how many elements (way points) there will be in the array. We can then ask the user to input X and Y values for each way point, and then calculate the overall distance. We will thus create a class VoyageDistance, and an applications class (voyageDistApp). A class diagram illustrating the proposed class structure is given in Figure 4.

CLASS DIAGRAM FOR VOYAGE DISTANCE PROBLEM

Figure 4: Class diagram for voyage distance problem


3.3 Design

The design of the two classes identified above is considered below.


3.3.1 VoyageDistance Class

Field Summary
private Point_2D[] voyage
           A private instance field to hold the "way points" for a "voyage" using an array of instances of the class Point_2D.
private int numWayPoints
           A private instance field to hold the number of "way points" making up a voyage description.
private static Scanner KeyboardInput
           A class instance to facilitate input from the input stream.

Constructor Summary
VoyageDistance(int numWP)
           Constructor to create an instance of the class VoyageDistance. Constructor initialises the numWayPoints field and dimensions the voyage array.

Method Summary
public void inputWayPointCoords()
           Public instance method to input the coordinates associated with a particular way point.
public double voyageDist()
           Public method to determine overall distance of voyage by looping through array of waypoints and calculting lengths through calls to the geoDist_2D method found in the Point_2D class.

A set of Nassi-Shneiderman diagrams for the VoyageDistance class is given in Figure 5.

NASSI-SHNEIDERMAN CHARTS FOR VOYAGE DISTANCE CLASS

Figure 5: Nassi-Shneiderman charts for voyage distance class.


3.3.2 VoyageDistApp Class

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

Method Summary
public static void main(String[] args)
           Top level method for voyage distance application. Call to inputNumWayPoints to allow user to input the "length" of the voyage (i.e. the number of way points). Create new instance of the class VoyageDistance. Call to inputWayPointCoords to allow user to input X and Y coordinates for individual way points. Call to voyageDist to calculate overall voyage distance.
private static int inputNumWayPoints()
           Method to allow the user to input the required number of way points. This must be at least two, thus error handling "do-while" loop included in case of erroneous input.

A series of Nassi Shneiderman charts describing the above methods are given in Figure 6.

NASSI SGNEIDERMAN CHARTS FOR VOYAGE DISTANCE APPLICATION CLASS

Figure 6: Nassi-Shneiderman charts for voyage distance application class.


3.4. Implementation

The implementation for the above is given in Tables 4 and 5.

// VOYAGE DISTANCE
// Frans Coenen
// Tuesday 21 November 2000
// Revised: Wednesday 7 Septermber 2005
// University of Liverpool

import java.util.*;

class VoyageDistance {

    // ---------------------- FIELDS ---------------------   
    
    private Point_2D[] voyage;
    private int numWayPoints;
    private static Scanner keyboardInput =  new Scanner(System.in);
	
    // ------------------ CONSTRUCTORS -------------------
    
    /* Constructor */
    
    public VoyageDistance(int numWP) {
        numWayPoints = numWP;
	
	// Define array of Point_2D instances
	
	voyage = new Point_2D[numWP];
	}
    
    /* Add way point X and Y values */
    
    public void inputWayPointCoords() {	
        final int START_POINT=0;
	int inputX, inputY;
	
	// Loop through way points inputting X ands Y values
	
	System.out.println("Way points = " + numWayPoints);
	for(int index = START_POINT;index < numWayPoints;index++) {
	    System.out.println("Input X and Y for way point " + (index+1));
	    inputX = keyboardInput.nextInt();
	    inputY = keyboardInput.nextInt();
	    voyage[index] = new Point_2D(inputX,inputY);
	    }
        }
    
    /* Calculate distance */
    
    public double voyageDist() {
        final int START_POINT=0;
	double total=0;
	
	// Loop through way points calculating each "leg" of the voyage.
	
	for(int index = START_POINT;index<(numWayPoints-1);index++) {
	    total = total + voyage[index].geoDist_2D(voyage[index+1]);
	    }
	
	// Return
	
	return(total);
	}    
    }

Table 4: Voyage distance class implementations

// VOYAGE DISTANCE APPLICATION
// Frans Coenen
// Wednesday 3 March 1999
// Revised: Wednesday 7 Septermber 2005
// University of Liverpool

import java.util.*;

class VoyageDistApp {

    // ------------------- FIELDS ------------------------               
    
    // Create Scanner class instance    
    private static Scanner keyboardInput = new Scanner(System.in);
		
    // ------------------ METHODS ------------------------  
	
    /* main method */
    
    public static void main(String args[]) {	
	
	// Input number of way points	
	int numWayPoints = inputNumWayPoints();
	
	// Create instance of classVoyage distance  
        VoyageDistance newVoyage = new VoyageDistance(numWayPoints);
	
	// Input Way point values	
	newVoyage.inputWayPointCoords();
	
	// Calculate distance and output distance	
	System.out.println("Voyage distance = " + newVoyage.voyageDist());
	}
	
    /* Input method */
    
    private static int inputNumWayPoints() { 
        final int MIN_LIMIT = 1;
        int inputWP;
	
	// Can not have one or less way points
	
	System.out.println("Input number of Way Points");
	do {
	    inputWP = keyboardInput.nextInt();
	    if (inputWP > MIN_LIMIT) break;
	    else System.out.println("ERROR 1: Input must be greater than " 
				+ MIN_LIMIT);
	    } while (true);
	
	// Return
	
	return(inputWP);
	}
    }	

Table 5: Voyage distance application class implementation


3.5. Testing

Black Box Testing: The code includes a limit at 1 therefore we should do some limit testing and BVA testing as shown in the table of test cases (right). Arithmetic testing dictates that we should try sample values which are negative, zero and positive. We can do this by specifying a number of squares comprising five way points such that the last way point is the same as the first (Figure 7). There is also nothing to prevent us from specifying a sequence of way points with identical coordiantes, the result of which should be a total voyage distance of 0. A sries of appropriate test cases is given in the table below. Note that the second case describes a 3-4-5 triangle.

TEST CASEEXPECTED RESULT
Num. way pointsCoordinatesDistance
1-Error
2{-1,-1} {2,3}5.0
5{0,0} {0,2} {2,2} {2,0} {0,0}8.0
5{1,1} {-1,1} {-1,-1} {1,-1} {1,1} 8.0
3{2,2} {2,2} {2,2}0.0
VOYAGE SQUARE

Figure 7: "Square" voyage

White Box testing: We should do some loop testing to exercise the loops in the program. The only variable count loop is the way point input loop and this is adequately tested uisng the BVA test cases given above. There are therefore no specific white box testcases to define.

Some sample output produced by running the above test cases is given in Table 6.

$ java VoyageDistApp  
Input number of Way Points
1
ERROR 1: Input must be greater than 1
2
Way points = 2
Input X and Y for way point 1
-1
-1
Input X and Y for way point 2
2
3
Voyage distance = 5.0

$ java VoyageDistApp
Input number of Way Points
5
Way points = 5
Input X and Y for way point 1
0
0
Input X and Y for way point 2
0
2
Input X and Y for way point 3
2
2
Input X and Y for way point 4
2
0
Input X and Y for way point 5
0
0
Voyage distance = 8.0

$ java VoyageDistApp
Input number of Way Points
5
Way points = 5
Input X and Y for way point 1
1
1
Input X and Y for way point 2
-1
1
Input X and Y for way point 3
-1
-1
Input X and Y for way point 4
1
-1
Input X and Y for way point 5
1
1
Voyage distance = 8.0         

$ java VoyageDistApp
Input number of Way Points
3
Way points = 3
Input X and Y for way point 1
2
2
Input X and Y for way point 2
2
2
Input X and Y for way point 3
2
2
Voyage distance = 0.0    

Table 6: Output from voyage distance application




4. NOTE ON AGGREGATION (COMPOSITION)

The idea of aggregations was introduced earlier. Now that we have a better understanding of the relationship between objects and classes it is worth briefly revisiting this topic.

We have seen that fields describe attributes and that these attributes can be class fields (keyword static) or instance fields. We have also noted that fields can be data items of any degree of complexity including other objects; we have studied mant examples where the class definition includes a field which is an instance of the class Scanner. We have also studied examples where we have made use of class methods and fields in other classes (for example the PI constant and sqrt method found in the Math class).

The practice of including instances of other classes as fields in the definition of a class is called aggregation or composition (there is a distinction but this is beyond the scope of this introductory module). A class diagram depicting aggregation is given in Figure 1.

Aggregation

Figure 1: Class diagram indicating aggregation (Remember that the notation used here, and throughtout the COMP101 notes) is not strictly UML)




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