Tutorial 9 Lecturer: Steve Bellantoni Tutor: Andria Hunter ========== 981127 @10am (RW117) @1pm (EM001) CSC108, Fall 1998 Tutorial notes, T9 ================================================================== Note ---- Note that the tutorial is held on Friday this week, instead of on Monday. Topics ------ - put any object on a stack - inheritance - pointer conversions - polymorphism - protected Overview -------- In this tutorial we will emphasise polymorphism, inheritance, and pointer conversions. These are relevant to assignment 4. The stack can contain various types of objects. Sample Program -------------- // This program is used to test various properties of pointers, // pointer conversions, polymorphism, and inheritance. // It includes stack classes from A4 (Stack, LinkedStack, LinkedStackElement) // the object classes (Animal, Armadillo, Hippogryph, Professor) and this // class that contains the main method (Tutorial9). import java.io.*; public class Tutorial9 { // The main method. // Constructs a LinkedStack object & puts six Animals in it. It then pops // objects off the stack to show various object oriented features of Java. // Notice that any type of object can be placed on the stack because all // objects extend the main Object class which is part of Java. public static void main( String[] args) throws IOException { // ----------- Testing Pushing any Object on Stack ----------- LinkedStack stack = new LinkedStack(); Hippogryph hippo1 = new Hippogryph ("George"); Hippogryph hippo2 = new Hippogryph ("Alice"); Professor prof1 = new Professor ("Sue"); Professor prof2 = new Professor ("Ian"); Armadillo arm1 = new Armadillo ("Harriot"); Armadillo arm2 = new Armadillo ("Harry"); // Not allowed for abstract classes //Animal animal = new Animal (); stack.push (hippo1); stack.push (arm1); stack.push (prof2); stack.push (hippo2); stack.push (arm2); stack.push (prof1); // ----------- Testing Inheritance ----------- // Note that if no toString method was defined for Animal, it would // use Object's toString method. System.out.println ("Stack initially contains..."); System.out.println (stack.toString()); // ----------- Testing Pointer Conversions ----------- // This would be legal, except that it would not find the // getType/getName methods because they aren't defined in Object. //Object obj_top = stack.pop (); //System.out.println ("Animal popped was " + obj_top.getType() + // " with name " + obj_top.getName()); Object obj_top = stack.pop (); System.out.println ("\nAnimal popped was ... " + obj_top.toString()); Animal ani_top = (Animal) stack.pop (); System.out.println ("\nAnimal popped was ... " + ani_top.getType() + " with name " + ani_top.getName()); Hippogryph hip_top = (Hippogryph) stack.pop (); System.out.println ("\nAnimal popped was ... " + hip_top.getType() + " with name " + hip_top.getName()); // This would generate "java.lang.ClassCastException: Armadillo" // runtime error. Can you explain why? //Hippogryph hip_top2 = (Hippogryph) stack.pop (); //System.out.println ("Animal popped was " + hip_top2.getType() + // " with name " + hip_top2.getName()); System.out.println ("\nAfter 3 pops, stack contains..."); System.out.println (stack.toString()); // ----------- Testing Polymorphism ----------- // Uses toString method in the Animal class System.out.println ("\nCall toString on Animal reference..."); System.out.println (ani_top.toString()); // Uses toString method in the Professor class Animal hold_ani_top = ani_top; Professor pro_top = (Professor) stack.pop (); ani_top = pro_top; System.out.println ("\nCall toString on Animal reference which " + "now refers to a Professor object..."); System.out.println (ani_top.toString()); ani_top = hold_ani_top; Armadillo arm_top = (Armadillo) ani_top; System.out.println ("\nCall toString on Armadillo reference which " + "now refers to a Armadillo object..."); System.out.println (arm_top.toString()); } // --- Animal: parent class ---- abstract class Animal { abstract public String getName(); abstract public String getType(); // Could not use the 'name' variable here, must use 'get...." methods // This method will override the Object's toString method. // Notice that there is no need for a toString method within each // subclass unless you wanted different printing for one subclass // (see how we have defined toString() for Professor). public String toString() { return "Animal: " + getType() + " named " + getName(); } } // --- Armadillo: subclass ---- class Armadillo extends Animal { private String name; Armadillo (String name) { this.name = name; } public String getName() { return name; } public String getType() { return "Armadillo"; } } // --- Hippogryph: subclass ---- class Hippogryph extends Animal { private String name; Hippogryph (String name) { this.name = name; } public String getName() { return name; } public String getType() { return "Hippogryph"; } } // --- Professor: subclass ---- class Professor extends Animal { private String name; Professor (String name) { this.name = name; } public String getName() { return name; } public String getType() { return "Professor"; } public String toString() { return "Person: " + getType() + " named " + getName(); } } Program Output -------------- Stack initially contains... [Person: Professor named Sue, Animal: Armadillo named Harry, Animal: Hippogryph named Alice, Person: Professor named Ian, Animal: Armadillo named Harriot, Animal: Hippogryph named George] Animal popped was ... Person: Professor named Sue Animal popped was ... Armadillo with name Harry Animal popped was ... Hippogryph with name Alice After 3 pops, stack contains... [Person: Professor named Ian, Animal: Armadillo named Harriot, Animal: Hippogryph named George] Call toString on Animal reference... Animal: Armadillo named Harry Call toString on Animal reference which now refers to a Professor object... Person: Professor named Ian Call toString on Armadillo reference which now refers to a Armadillo object... Animal: Armadillo named Harry Discussion ---------- --- Pushing objects onto the stack --- We can push various kinds of animals -- Hippogryphs, Armadillos, Professors -- onto a stack. The stack routines are definted to take an Object as a parameter, and since all object that we create in Java extend the Object class, we can push any object onto the stack. Another example occurs in the testing routine for assignment 4, where we push one stack onto another. -> Notice that when the artists stack in assigment 4 is printed it looks like this at one point in the output: [Caravaggio, [7, 5, 3, 2], Andy W., Simone M., Pablo P., Frida K.] -> This is because one of the elements pushed on the stack was the 'primes' stack, which is just another object. artists.push (primes); artists.push ("Caravaggio"); --- Inheritance --- Inheritance: the automatic presence of functions and data items from the base class in the derived class. Notice that when we print each element in our stack, it uses the toString method that has been defined in the Animal class, except when it prints out Professors ... in this case the toString method in the Professor class overrides the toString method in the Animal class. What would happen if the Animal and Professor classes did not contain toString methods? It would use the toString method that has been defined in the Object parent class. The output would look like this... Stack initially contains... [Professor@16c77f, Armadillo@16c783, Hippogryph@16c77d, Professor@16c780, Armadillo@16c782, Hippogryph@16c77c] Animal popped was ... Professor@16c77f Animal popped was ... Armadillo with name Harry Animal popped was ... Hippogryph with name Alice --- Pointer Conversions --- When we take the animals off the stack we cast the return value of pop() into an Animal pointer. We can use Animal methods on everything in the stack, even without knowing what particular type of Animals they are. Pointer conversions from base type to derived type require explicit casts, while conversions in the other direction do not. --- Polymorphism --- Polymorphism: the ability to refer to objects of many different (derived) types using a pointer of a single (base) type. pg. 315: The ability of references of one class to refer to an object of another class is the key to polymorphism. When a reference is used to invoke a method, the class of the object being refered to determines which method is invoked, not the type of the reference. Animal a = ... Professor p = ... a = p; // no casting needed here since p extends a System.out.println (a.toString()); // will use Professor's toString In this case a.toString() is a polymorphic expression. ----- Note that an explicit cast is required to assign the base class (a) to the subclass (p): Animal a = ... Professor p = ... p = (Professor) a; System.out.println (p.toString()); // will use Professor's toString --- Protected --- Describe use of "protected" keyword and suggest that it should be avoided when possible. People deriving from the class usually should not have more privileges than other users of the class.