Start small

by Charles Kelemen


Under construction. Check back in a week.

Here is our challange.

Suppose we are asked to help a college with a small part of its data processing needs. The college has a file containing records on students, staff, and faculty. Each record has fields as follows:

Here is a small portion of the file:

stu William 183 1999 Kelemen Mertz stf Don 640 12 stf Bill 643 12 stu Gabriel 180 2002 Kelemen Parrish fac Kelemen 520 CS 8123 stu Brandon 176 2000 Kelemen ML

Each record in the file begins with a 3 letter code indicating whether it is a student(stu), staff(stf), or faculty(fac) record. The file is in no particular order.

We would like to be able to sort these records in different ways. For example, we might want them all in ascending order by name for a phone directory. we might want them in ascending order by ID number for some other purpose.

We have developed a sort Sort.inssort that can sort any array of Sortable objects. So our task will be to make our collection of Student, Faculty, and Staff objects into an array of Sortable objects with an appropriate precedes methods for the ordering we desire. We can redefine precedes for different orderings.

Looking at the fields of these records we notice that they all have a name and ID number in common. This is a tip off that we should define a superclass (we'll call it Person) that contains a name and ID field. Then we will have classes Student, Staff, and Faculty be subclasses of Person. If we make Person implement Sortable, then any object of type Student, Staff, or Faculty may be considered a member of the class Person or of the class Sortable. Then if we define precedes methods in Student, Staff, and Faculty, we should be able to use Sort.inssort.

Now that we have the overall idea, we will begin to code. We do NOT have to write all of this at once. In fact I recommend starting with jaust a few lines of code and working in small increments. That is provide enough scaffolding in your code to be able to know what is going on. then compile, run,. and debug, in small increments. This provides frequent positive feedback. The scaffolding can be removed later.

So, how can we begin small? We could write just one of the subclasses, work with it and then write the superclass and other subclasses. Alternatively, we could write Person, work with it, and then write the subclasses. We will do that latter.

Here is a start for the class Person (stored in file: Person.java):

thyme.cs.swarthmore.edu% cat Person.java // Soon to be a suprclass, but now just a small class // by cfk // import java.io.*; class Person { // protected data members.... protected String name; protected int idnum; // contructors public Person() {name=""; idnum=0;} public Person(String n, int id) { name=new String(n); idnum=id;} // public MEMBER FUNCTIONS ... // display public void display() { System.out.print(" Name: " + name + " "); System.out.println(" Id: " + idnum); } }

Here is a small test program to try our Person class. The test program is in the file: Trysubcl.java.

thyme.cs.swarthmore.edu% cat Trysubcl.java // A program to help illustrate building subclasses starting small // by cfk // This example justs tests a class without any subclass import java.io.*; class Trysubcl { public static void main(String argv[]) { int idno; String nm; Person p1, p2; //declare a couple of Persons System.out.println("starting: "); // let's just try to create a couple of Persons using // the constructor and display them using their display //methods idno=1; nm="Washington"; p1 = new Person(nm, idno); //instantiate a Person idno=2; nm="Adams"; p2 = new Person(nm, idno); //instantiate a second Person System.out.println("here are people"); p1.display(); System.out.println(); p2.display(); } }

Putting these two files in a common directory and compiling lead to a minor syntactical error. Because the files were so small, it was easy to find the error and fix it. Then compiling and executing gave:

thyme.cs.swarthmore.edu% javac Trysubcl.java thyme.cs.swarthmore.edu% java Trysubcl starting: here are people Name: Washington Id: 1 Name: Adams Id: 2

In Trysubcl, we created two objects of type Person. Each object was created using its own constructor. The class Person contains an instance method display(). Each object used its own display() method to create the output.

Let's now add the subclass Staff. After we get stuff working for Staff, we can add Students and Faculty. The file Person.java remains the same. Here is the file Staff.java and the slightly updated Trysubcl.java.

thyme.cs.swarthmore.edu% cat Staff.java // // by cfk import java.io.*; class Staff extends Person { // private data members.... private int months; // contructors public Staff() {name=""; idnum=0; months=0;} public Staff(String n, int id, int mo) { name=new String(n); // name inherited from Person idnum=id; //idnum inherited from person months=mo; } // public MEMBER FUNCTIONS ... // display public void display() { System.out.print(" Name: " + name); System.out.print(" Id: " + idnum); System.out.println(" Months: " + months); } } thyme.cs.swarthmore.edu% cat Trysubcl.java // A program to help illustrate building subclasses starting small // by cfk // We add the Class Staff which is a subclass of Person import java.io.*; class Trysubcl { public static void main(String argv[]) { int idno; String nm; Person p1, p2; //declare a couple of Persons Staff s1, s2; // declare a couple of Staff System.out.println("starting: "); // let's just try to create a couple of Persons using // the constructor and display them using their display //methods idno=1; nm="Washington"; p1 = new Person(nm, idno); //instantiate a Person idno=2; nm="Adams"; p2 = new Person(nm, idno); //instantiate a second Person // now let's create a couple of Staff objects idno=85; nm="Franklin"; s1 = new Staff(nm, idno, 30); //instantiate a Staff s2 = new Staff(new String("Jefferson"), 25, 7); // another Staff System.out.println("here are people"); p1.display(); p2.display(); s1.display(); s2.display(); } }

Here is the directory where these files reside and the results of compiling and executing Trysubcl.java.

thyme.cs.swarthmore.edu% ls Person.java Staff.java Trysubcl.java thyme.cs.swarthmore.edu% javac Trysubcl.java thyme.cs.swarthmore.edu% java Trysubcl starting: here are people Name: Washington Id: 1 Name: Adams Id: 2 Name: Franklin Id: 85 Months: 30 Name: Jefferson Id: 25 Months: 7

Since Staff extends Person, s1 and s2 can be regarded as either Staff objects or Person objects. This means that there are two candidate display() methods that might be used at s1.display(); Because the signatures are the same the display() method in Staff 'overrides' the display() method in Person. This is exactly what we want and the Staff objects are displayed properly showing their months of service.

We are now ready to define a heterogeneous array. Since s1 and s2 can be regarded as Person objects, they can be stored in an array of Person along with p1 and p2. Files Person.java and Staff.java remain unchanged. Here is the new Trysubcl.java:

thyme.cs.swarthmore.edu% cat Trysubcl.java // A program to help illustrate building subclasses starting small // by cfk // We add an array of Person holding references to both Staff objects // and simple Person objects. This is a heterogeneous array. import java.io.*; class Trysubcl { public static void main(String argv[]) { int idno; String nm; Person p1, p2; //declare a couple of Persons Staff s1, s2; // declare a couple of Staff Person pers[]; //declare array of class Person pers = new Person[4]; //instantiate array of references // to Person objects System.out.println("starting: "); // let's just try to create a couple of Persons using // the constructor and display them using their display //methods idno=1; nm="Washington"; p1 = new Person(nm, idno); //instantiate a Person idno=2; nm="Adams"; p2 = new Person(nm, idno); //instantiate a second Person // now let's create a couple of Staff objects idno=85; nm="Franklin"; s1 = new Staff(nm, idno, 30); //instantiate a Staff s2 = new Staff(new String("Jefferson"), 25, 7); // another Staff pers[0] = p1; pers[1] = p2; pers[2] = s1; // Staff extends Person so this is ok. pers[3] = s2; System.out.println("here are people"); for (int i=0; i<4; i++) pers[i].display(); } }

Compiling and running this version leads to:

thyme.cs.swarthmore.edu% java Trysubcl starting: here are people Name: Washington Id: 1 Name: Adams Id: 2 Name: Franklin Id: 85 Months: 30 Name: Jefferson Id: 25 Months: 7

This is the same result that we had before and what we wanted. The output came from the loop:

for (int i=0; i<4; i++) pers[i].display();

It is important to understand that even though pers[] is declared to be an array of Person, the Java compiler and run time system dynamically bind the appropriate display() method at each invocation at execution time. In this case pers[1] is a Person object and the display() method declared in the class Person is used for pers[1]. pers[2] is a Staff object and the display() method declared in the class Staff is used for pers[2].

Now that we have a small array, how can we sort it? Our sort program works for arrays of Sortable objects. Copy Sort.java and Sortable.java to your current directory. It is important to note that we will make no changes to either Sort.java or Sortable.java. We will change Person.java by adding the phrase "implements Sortable" to the Class declaration and providing the instance method precedes((Object other). For the present we will define precedes so that the sort will be by names in alphabetical order. Here is the file Person.java.

thyme.cs.swarthmore.edu% cat Person.java // a superclass for Student, Staff, and Faculty // by cfk // import java.io.*; class Person implements Sortable { // protected data members.... protected String name; protected int idnum; // contructors public Person() {name=""; idnum=0;} public Person(String n, int id) { name=new String(n); idnum=id;} // public MEMBER FUNCTIONS ... //comparison // use lexicographic order by name field public boolean precedes(Object other) { return(this.name.compareTo( ((Person) other).name) < 0); } // display public void display() { System.out.print(" Name: " + name + " "); System.out.println(" Id: " + idnum); } }

We also alter Trysubcl.java, invoking Sort.inssort(pers, 4); and printing the array before and after the sort.

thyme.cs.swarthmore.edu% cat Trysubcl.java // A program to help illustrate building subclasses starting small // by cfk // We add an array of Person holding references to both Staff objects // and simple Person objects. This is a heterogeneous array. // Now we invoke Sort.inssort() on it. import java.io.*; class Trysubcl { public static void main(String argv[]) { int idno; String nm; Person p1, p2; //declare a couple of Persons Staff s1, s2; // declare a couple of Staff Person pers[]; //declare array of class Person pers = new Person[4]; //instantiate array of references // to Person objects System.out.println("starting: "); // let's just try to create a couple of Persons using // the constructor and display them using their display //methods idno=1; nm="Washington"; p1 = new Person(nm, idno); //instantiate a Person idno=2; nm="Adams"; p2 = new Person(nm, idno); //instantiate a second Person // now let's create a couple of Staff objects idno=85; nm="Franklin"; s1 = new Staff(nm, idno, 30); //instantiate a Staff s2 = new Staff(new String("Jefferson"), 25, 7); // another Staff pers[0] = p1; pers[1] = p2; pers[2] = s1; // Staff extends Person so this is ok. pers[3] = s2; System.out.println("here are people before sorting"); for (int i=0; i<4; i++) pers[i].display(); Sort.inssort(pers, 4); System.out.println("here are people after sorting"); for (int i=0; i<4; i++) pers[i].display(); } }

After compiling and executing we get:

thyme.cs.swarthmore.edu% java Trysubcl starting: here are people before sorting Name: Washington Id: 1 Name: Adams Id: 2 Name: Franklin Id: 85 Months: 30 Name: Jefferson Id: 25 Months: 7 here are people after sorting Name: Adams Id: 2 Name: Franklin Id: 85 Months: 30 Name: Jefferson Id: 25 Months: 7 Name: Washington Id: 1

As you can see Sort.inssort(pers, 4) has rearranged the pers[] array so that all the people (both Staff and Person) are in lexicographic order by name. Let's think about this a bit. Sort.inssort uses the method precedes promised by the interface Sortable to compare and order the elements of the array. The interface Sortable looks like:

public interface Sortable { public boolean precedes(Object ob); } The parameter, ob, to precedes is an Object to make it as general as possible. In Java, all classes (but not primitive types) are subclasses of Object. To make Person 'implement' Sortable, we must provide a method precedes in Person with exactly the same signature. The method we used is: public boolean precedes(Object other) { return(this.name.compareTo( ((Person) other).name) < 0); }

Since this method is in Person, we assume that the formal parameter 'other' can be cast to Person. A general Object may not have a name field, but a Person object does. Thus, this.name is a String and ((Person) other).name is also a String so we can use the String instance method compareTo to compare them.

To make sure you understand this, modify the precedes method so that the sort arranges the people in descending order by idnum. The only thing you should have to change is the return line in the method precedes in the class Person.

We have been working in small increments and getting lots of CS highs as we see things work. We can now move to implementing the subclasses Student and Faculty or we can tackle file input. Let's do file input first. We will ultimately want to be able to handle large files of Student, Faculty, and Staff. But for now, let's just imagine a small file like:

6 stf Jeff 651 9 per Timothy 101 per Jimmy 103 stf Don 640 12 stf Bill 643 12 per Gabriel 180 We have a design decision to make. Should the main program do the input from the file and then use constructors to put the data into the object's fields or should the main program determine what kind of object and let an instance method of the object complete the input? Either choice is reasonable, but I like latter. So the main program will set up a StreamTokenizer that will read from the file. For each record the main program will determine whether it is a Person or Staff. The main program will then create an object of the appropriate class and invoke the file_in instance method of that object. The only change to the files Person.java and Staff.java is to add the instance method // file input public void file_in(StreamTokenizer inf) throws IOException { inf.nextToken(); name = inf.sval; inf.nextToken(); idnum=(int)inf.nval; } to Person.java and the method // file input public void file_in(StreamTokenizer inf) throws IOException { inf.nextToken(); name = inf.sval; inf.nextToken(); idnum=(int)inf.nval; inf.nextToken(); months = (int)inf.nval; } to the file Staff.java. With these changes to Person and Staff, the following file Frysubcl.java will read in and print out the file above.

thyme.cs.swarthmore.edu% cat Trysubcl.java // A program to help illustrate building subclasses starting small // by cfk // We now introduce reading from a file into our heterogeneous // array of Persons. The main program determines the kind // of object a file record contains and the the an object // instance of the appropriate class completes reading // the record from the file. // import java.io.*; class Trysubcl { public static void main(String argv[]) throws IOException { // Set up a StreamTokenizer associated with our input file FileReader infi=new FileReader("students.txt"); StreamTokenizer infitok = new StreamTokenizer(infi); infitok.eolIsSignificant(false); int numofpeople; //the number of people in the file String kindofperson; // the kind of person this line holds System.out.println("starting: "); infitok.nextToken(); numofpeople=(int)infitok.nval; Person pers[]; //declare array of class Person pers = new Person[numofpeople]; //instantiate array of references // to Person objects // read and print each record from the file for (int i=0; i<numofpeople; i++) { infitok.nextToken(); kindofperson=infitok.sval; if ( kindofperson.equalsIgnoreCase("per") ) { pers[i]=new Person(); //instantiate object that reference temp //points to (Java purists do not like the p word) pers[i].file_in(infitok); // use objects file_in method to initialize } if ( kindofperson.equalsIgnoreCase("stf") ) { pers[i]=new Staff(); //instantiate object that reference temp //points to (Java purists do not like the p word) pers[i].file_in(infitok); // use objects file_in method to initialize } pers[i].display(); } } }

thyme.cs.swarthmore.edu% cat students.txt 6 stf Jeff 651 9 per Timothy 101 per Jimmy 103 stf Don 640 12 stf Bill 643 12 per Gabriel 180 thyme.cs.swarthmore.edu% java Trysubcl starting: Name: Jeff Id: 651 Months: 9 Name: Timothy Id: 101 Name: Jimmy Id: 103 Name: Don Id: 640 Months: 12 Name: Bill Id: 643 Months: 12 Name: Gabriel Id: 180 thyme.cs.swarthmore.edu%

Now that we can read it in, we'll invoke our old sort without changes to anything but the main program which now looks like:

thyme.cs.swarthmore.edu% cat Trysubcl.java // A program to help illustrate building subclasses starting small // by cfk // We now introduce reading from a file into our heterogeneous // array of Persons. The main program determines the kind // of object a file record contains and the the an object // instance of the appropriate class completes reading // the record from the file. // import java.io.*; class Trysubcl { public static void main(String argv[]) throws IOException { // Set up a StreamTokenizer associated with our input file FileReader infi=new FileReader("students.txt"); StreamTokenizer infitok = new StreamTokenizer(infi); infitok.eolIsSignificant(false); int numofpeople; //the number of people in the file String kindofperson; // the kind of person this line holds System.out.println("starting: "); infitok.nextToken(); numofpeople=(int)infitok.nval; Person pers[]; //declare array of class Person pers = new Person[numofpeople]; //instantiate array of references // to Person objects // read and print each record from the file for (int i=0; i<numofpeople; i++) { infitok.nextToken(); kindofperson=infitok.sval; if ( kindofperson.equalsIgnoreCase("per") ) { pers[i]=new Person(); //instantiate object pers[i].file_in(infitok); // use objects file_in method to initialize } if ( kindofperson.equalsIgnoreCase("stf") ) { pers[i]=new Staff(); //instantiate object pers[i].file_in(infitok); // use objects file_in method to initialize } } System.out.println("have read in people"); for (int k=0; k<numofpeople; k++) pers[k].display(); Sort.inssort(pers, numofpeople); // call class sort method of Person System.out.println("after sorting people"); for (int k=0; k<numofpeople; k++) pers[k].display(); } }

After compiling and running, we get:

thyme.cs.swarthmore.edu% java Trysubcl starting: have read in people Name: Jeff Id: 651 Months: 9 Name: Timothy Id: 101 Name: Jimmy Id: 103 Name: Don Id: 640 Months: 12 Name: Bill Id: 643 Months: 12 Name: Gabriel Id: 180 after sorting people Name: Bill Id: 643 Months: 12 Name: Don Id: 640 Months: 12 Name: Gabriel Id: 180 Name: Jeff Id: 651 Months: 9 Name: Jimmy Id: 103 Name: Timothy Id: 101 thyme.cs.swarthmore.edu%

Now that we have file input and sorting working for objects of Person and Staff, we will add the classes Students and Faculty.

thyme.cs.swarthmore.edu% cat Student.java // // by cfk import java.io.*; class Student extends Person { // private data members.... private String advisor, dorm; private int gradyear; // contructors public Student() {name=""; gradyear=0;idnum=0;advisor="";dorm="";} // public MEMBER FUNCTIONS ... // display public void display() { System.out.print(" Name: " + name); System.out.print(" Id: " + idnum); System.out.print(" Grad year: " + gradyear); System.out.print(" Advisor: " + advisor); System.out.println(" Dorm: " + dorm); } // file input public void file_in(StreamTokenizer inf) throws IOException { inf.nextToken(); name = inf.sval; inf.nextToken(); idnum=(int)inf.nval; inf.nextToken(); gradyear=(int)inf.nval; inf.nextToken(); advisor = inf.sval; inf.nextToken(); dorm = inf.sval; } } thyme.cs.swarthmore.edu% cat Faculty.java // // by cfk import java.io.*; class Faculty extends Person { // private data members.... private String dept; private int phone; // contructors public Faculty() {name=""; idnum=0; dept=""; phone=0;} // public MEMBER FUNCTIONS ... // display public void display() { System.out.print(" Name: " + name); System.out.print(" Id: " + idnum); System.out.print(" Dept: " + dept); System.out.println(" Phone: " + phone); } // file input public void file_in(StreamTokenizer inf) throws IOException { inf.nextToken(); name = inf.sval; inf.nextToken(); idnum=(int)inf.nval; inf.nextToken(); dept=inf.sval; inf.nextToken(); phone= (int)inf.nval; } }

With these two new classes and by adding

if ( kindofperson.equalsIgnoreCase("stu") ) { pers[i]=new Student(); //instantiate object pers[i].file_in(infitok); // use objects file_in method to initialize } if ( kindofperson.equalsIgnoreCase("fac") ) { pers[i]=new Faculty(); //instantiate object pers[i].file_in(infitok); // use objects file_in method to initialize } to the input for-loop in Trysubcl.java, we can read in and sort the file student.txt

thyme.cs.swarthmore.edu% cat students.txt 29 stu Nii 66 2002 smith Hollowell fac Pete 567 English 8888 stu Laura 34 2000 Meeden Dana fac Sue 535 Math 8887 stu Aaron 36 2001 Marshall Wharton stu Bjorn 41 2001 Kelemen Palmer stu Kellen 28 2002 Kelemen Pitt stf Jeff 651 9 stu Timothy 101 2000 Kelemen Palmer stu Jimmy 103 2001 Kelemen Parrish stu Joshua 106 2000 Meeden ML stu YingJie 108 2001 Meeden Parrish fac Marshall 501 CS 8765 stu Will 113 2000 Kelemen Mertz stu Benjamin 116 2002 Meeden ML stu Benjamin 119 2001 Marshall Parrish stu William 183 1999 Kelemen Mertz stf Don 640 12 stf Bill 643 12 stu Gabriel 180 2002 Kelemen Parrish fac Kelemen 520 CS 8123 stu Brandon 176 2000 Kelemen ML stu Thomas 173 2002 Kelemen Parrish stu Eugene 170 2001 Meeden Willets stu Stephanie 168 2002 Marshall ML stu Jeffrey 165 2001 Kelemen Mertz stf Joan 677 9 stu Yuhai 164 2001 Kelemen Mertz fac Meeden 530 CS 8333

We get:

thyme.cs.swarthmore.edu% java Trysubcl starting: have read in people Name: Nii Id: 66 Grad year: 2002 Advisor: smith Dorm: Hollowell Name: Pete Id: 567 Dept: English Phone: 8888 Name: Laura Id: 34 Grad year: 2000 Advisor: Meeden Dorm: Dana Name: Sue Id: 535 Dept: Math Phone: 8887 Name: Aaron Id: 36 Grad year: 2001 Advisor: Marshall Dorm: Wharton Name: Bjorn Id: 41 Grad year: 2001 Advisor: Kelemen Dorm: Palmer Name: Kellen Id: 28 Grad year: 2002 Advisor: Kelemen Dorm: Pitt Name: Jeff Id: 651 Months: 9 Name: Timothy Id: 101 Grad year: 2000 Advisor: Kelemen Dorm: Palmer Name: Jimmy Id: 103 Grad year: 2001 Advisor: Kelemen Dorm: Parrish Name: Joshua Id: 106 Grad year: 2000 Advisor: Meeden Dorm: ML Name: YingJie Id: 108 Grad year: 2001 Advisor: Meeden Dorm: Parrish Name: Marshall Id: 501 Dept: CS Phone: 8765 Name: Will Id: 113 Grad year: 2000 Advisor: Kelemen Dorm: Mertz Name: Benjamin Id: 116 Grad year: 2002 Advisor: Meeden Dorm: ML Name: Benjamin Id: 119 Grad year: 2001 Advisor: Marshall Dorm: Parrish Name: William Id: 183 Grad year: 1999 Advisor: Kelemen Dorm: Mertz Name: Don Id: 640 Months: 12 Name: Bill Id: 643 Months: 12 Name: Gabriel Id: 180 Grad year: 2002 Advisor: Kelemen Dorm: Parrish Name: Kelemen Id: 520 Dept: CS Phone: 8123 Name: Brandon Id: 176 Grad year: 2000 Advisor: Kelemen Dorm: ML Name: Thomas Id: 173 Grad year: 2002 Advisor: Kelemen Dorm: Parrish Name: Eugene Id: 170 Grad year: 2001 Advisor: Meeden Dorm: Willets Name: Stephanie Id: 168 Grad year: 2002 Advisor: Marshall Dorm: ML Name: Jeffrey Id: 165 Grad year: 2001 Advisor: Kelemen Dorm: Mertz Name: Joan Id: 677 Months: 9 Name: Yuhai Id: 164 Grad year: 2001 Advisor: Kelemen Dorm: Mertz Name: Meeden Id: 530 Dept: CS Phone: 8333 after sorting people Name: Aaron Id: 36 Grad year: 2001 Advisor: Marshall Dorm: Wharton Name: Benjamin Id: 116 Grad year: 2002 Advisor: Meeden Dorm: ML Name: Benjamin Id: 119 Grad year: 2001 Advisor: Marshall Dorm: Parrish Name: Bill Id: 643 Months: 12 Name: Bjorn Id: 41 Grad year: 2001 Advisor: Kelemen Dorm: Palmer Name: Brandon Id: 176 Grad year: 2000 Advisor: Kelemen Dorm: ML Name: Don Id: 640 Months: 12 Name: Eugene Id: 170 Grad year: 2001 Advisor: Meeden Dorm: Willets Name: Gabriel Id: 180 Grad year: 2002 Advisor: Kelemen Dorm: Parrish Name: Jeff Id: 651 Months: 9 Name: Jeffrey Id: 165 Grad year: 2001 Advisor: Kelemen Dorm: Mertz Name: Jimmy Id: 103 Grad year: 2001 Advisor: Kelemen Dorm: Parrish Name: Joan Id: 677 Months: 9 Name: Joshua Id: 106 Grad year: 2000 Advisor: Meeden Dorm: ML Name: Kelemen Id: 520 Dept: CS Phone: 8123 Name: Kellen Id: 28 Grad year: 2002 Advisor: Kelemen Dorm: Pitt Name: Laura Id: 34 Grad year: 2000 Advisor: Meeden Dorm: Dana Name: Marshall Id: 501 Dept: CS Phone: 8765 Name: Meeden Id: 530 Dept: CS Phone: 8333 Name: Nii Id: 66 Grad year: 2002 Advisor: smith Dorm: Hollowell Name: Pete Id: 567 Dept: English Phone: 8888 Name: Stephanie Id: 168 Grad year: 2002 Advisor: Marshall Dorm: ML Name: Sue Id: 535 Dept: Math Phone: 8887 Name: Thomas Id: 173 Grad year: 2002 Advisor: Kelemen Dorm: Parrish Name: Timothy Id: 101 Grad year: 2000 Advisor: Kelemen Dorm: Palmer Name: Will Id: 113 Grad year: 2000 Advisor: Kelemen Dorm: Mertz Name: William Id: 183 Grad year: 1999 Advisor: Kelemen Dorm: Mertz Name: YingJie Id: 108 Grad year: 2001 Advisor: Meeden Dorm: Parrish Name: Yuhai Id: 164 Grad year: 2001 Advisor: Kelemen Dorm: Mertz By working in small steps, we can now sort a heterogeneous array of Person. Some of the objects are Students with 5 fields, some Faculty with 4 fields, and some Staff with 3 fields. By introducing more sophisticated precedes methods in the individual subclasses, and exploiting the fact that Java will override the Person precedes with a subclass precedes of the same signature, we can achieve very sophisticated Sorts without changing our Sort method or anything else in our classes. In order to do this we will use the Java operator instanceof. I ob is an object and Cls is a class, then ob instanceof Cls returns true if ob is an instance of class Cls and return false otherwise. Suppose we are interested in sorting our heterogeneous array of Person as follows: Staff come first in alphabetical order by name; Students come next in ascending order by Id number; Faculty come last in alphabetical order by name. We can do this by adding approptriate precedes methods to Staff, Student, and Faculty. We will not change the files Sort, Sortable, Person, or Trysubcl. Here is what we will add to Staff:

public boolean precedes(Object other) { if (other instanceof Staff) return(this.name.compareTo(((Staff)other).name) < 0); else return(true); } Suppose st is a Staff object an ob is some other object (could be Staff, Student, Faculty or something else). Then st.precedes(ob) will use the precedes method for the Staff instance st (overridding the precedes in Person). st.precedes(ob) will check whether ob is also an instance of Staff. If not, st.precedes(ob) returns true making st precede an object of any type other than Staff. If ob is of type Staff, then the names are compared to determine precedence. If the Staff objects were the only objects to check precedence, then this would suffice to put all Staff objects first. But Staff objects are NOT the only objects to check precedence. Student and Faculty objects will also invoke precedes so we must override the Person precedes in both Student and Faculty. Here is precedes for Student:

public boolean precedes(Object other) { if (other instanceof Student) return (this.idnum< ((Student)other).idnum); else if (other instanceof Staff) return(false); else return(true); } Now suppose st is a Student object an ob is some other object (could be Staff, Student, Faculty, or something else). Then st.precedes(ob) will use the precedes method for the Student instance st (overridding the precedes in Person). st.precedes(ob) will check whether ob is also an instance of Student. If so, st.precedes(ob) determines precedence on the basis of ID numbers. If not, st.precedes(ob) returns false if ob is a Staff object (making all Students come after Staff when a Student object is doing the comparison). Finally if ob is not a Student or a Staff object, st.precedes(ob) returns true making st precede any object of any type other than Student or Staff (if a Student is doing the precedes). So now, whether a Staff object or a Student object does the precedes, Staff objects will precede Student objects. Staff objects will determine precedence with other Staff objects by name. Student objects will determine precedence with other Student objects by ID number. To complete our ordering we must override the Person precedes Faculty. Here is precedes for Faculty:

public boolean precedes(Object other) { if (other instanceof Faculty) return(this.name.compareTo(((Faculty)other).name) < 0); else return(false); } Once these precedes methods are inserted into Staff, Student, and Faculty, compiling and running Trysubcl leads to:

thyme.cs.swarthmore.edu% java Trysubcl starting: have read in people Name: Nii Id: 66 Grad year: 2002 Advisor: smith Dorm: Hollowell Name: Pete Id: 567 Dept: English Phone: 8888 Name: Laura Id: 34 Grad year: 2000 Advisor: Meeden Dorm: Dana Name: Sue Id: 535 Dept: Math Phone: 8887 Name: Aaron Id: 36 Grad year: 2001 Advisor: Marshall Dorm: Wharton Name: Bjorn Id: 41 Grad year: 2001 Advisor: Kelemen Dorm: Palmer Name: Kellen Id: 28 Grad year: 2002 Advisor: Kelemen Dorm: Pitt Name: Jeff Id: 651 Months: 9 Name: Timothy Id: 101 Grad year: 2000 Advisor: Kelemen Dorm: Palmer Name: Jimmy Id: 103 Grad year: 2001 Advisor: Kelemen Dorm: Parrish Name: Joshua Id: 106 Grad year: 2000 Advisor: Meeden Dorm: ML Name: YingJie Id: 108 Grad year: 2001 Advisor: Meeden Dorm: Parrish Name: Marshall Id: 501 Dept: CS Phone: 8765 Name: Will Id: 113 Grad year: 2000 Advisor: Kelemen Dorm: Mertz Name: Benjamin Id: 116 Grad year: 2002 Advisor: Meeden Dorm: ML Name: Benjamin Id: 119 Grad year: 2001 Advisor: Marshall Dorm: Parrish Name: William Id: 183 Grad year: 1999 Advisor: Kelemen Dorm: Mertz Name: Don Id: 640 Months: 12 Name: Bill Id: 643 Months: 12 Name: Gabriel Id: 180 Grad year: 2002 Advisor: Kelemen Dorm: Parrish Name: Kelemen Id: 520 Dept: CS Phone: 8123 Name: Brandon Id: 176 Grad year: 2000 Advisor: Kelemen Dorm: ML Name: Thomas Id: 173 Grad year: 2002 Advisor: Kelemen Dorm: Parrish Name: Eugene Id: 170 Grad year: 2001 Advisor: Meeden Dorm: Willets Name: Stephanie Id: 168 Grad year: 2002 Advisor: Marshall Dorm: ML Name: Jeffrey Id: 165 Grad year: 2001 Advisor: Kelemen Dorm: Mertz Name: Joan Id: 677 Months: 9 Name: Yuhai Id: 164 Grad year: 2001 Advisor: Kelemen Dorm: Mertz Name: Meeden Id: 530 Dept: CS Phone: 8333 after sorting people Name: Bill Id: 643 Months: 12 Name: Don Id: 640 Months: 12 Name: Jeff Id: 651 Months: 9 Name: Joan Id: 677 Months: 9 Name: Kellen Id: 28 Grad year: 2002 Advisor: Kelemen Dorm: Pitt Name: Laura Id: 34 Grad year: 2000 Advisor: Meeden Dorm: Dana Name: Aaron Id: 36 Grad year: 2001 Advisor: Marshall Dorm: Wharton Name: Bjorn Id: 41 Grad year: 2001 Advisor: Kelemen Dorm: Palmer Name: Nii Id: 66 Grad year: 2002 Advisor: smith Dorm: Hollowell Name: Timothy Id: 101 Grad year: 2000 Advisor: Kelemen Dorm: Palmer Name: Jimmy Id: 103 Grad year: 2001 Advisor: Kelemen Dorm: Parrish Name: Joshua Id: 106 Grad year: 2000 Advisor: Meeden Dorm: ML Name: YingJie Id: 108 Grad year: 2001 Advisor: Meeden Dorm: Parrish Name: Will Id: 113 Grad year: 2000 Advisor: Kelemen Dorm: Mertz Name: Benjamin Id: 116 Grad year: 2002 Advisor: Meeden Dorm: ML Name: Benjamin Id: 119 Grad year: 2001 Advisor: Marshall Dorm: Parrish Name: Yuhai Id: 164 Grad year: 2001 Advisor: Kelemen Dorm: Mertz Name: Jeffrey Id: 165 Grad year: 2001 Advisor: Kelemen Dorm: Mertz Name: Stephanie Id: 168 Grad year: 2002 Advisor: Marshall Dorm: ML Name: Eugene Id: 170 Grad year: 2001 Advisor: Meeden Dorm: Willets Name: Thomas Id: 173 Grad year: 2002 Advisor: Kelemen Dorm: Parrish Name: Brandon Id: 176 Grad year: 2000 Advisor: Kelemen Dorm: ML Name: Gabriel Id: 180 Grad year: 2002 Advisor: Kelemen Dorm: Parrish Name: William Id: 183 Grad year: 1999 Advisor: Kelemen Dorm: Mertz Name: Kelemen Id: 520 Dept: CS Phone: 8123 Name: Marshall Id: 501 Dept: CS Phone: 8765 Name: Meeden Id: 530 Dept: CS Phone: 8333 Name: Pete Id: 567 Dept: English Phone: 8888 Name: Sue Id: 535 Dept: Math Phone: 8887 This is what we wanted and we got it without using the same tried and true Sort we developed for any Sortable object.