Chapter 9, Inheritance, Abstract Classes, Interfaces and Generics Earlier we identified some of the objects in the description of a payroll system for the Crunchy Crème Cupcake Factory. We identified that we would need to calculate pay for Salaried Employees, Hourly Employees, and Commission-based Employees. We identified some basic features that all types of employees share in common: first name, last name, title, hire date, address, phone # etc. , and features specific to each subtype: Such as only Hourly employees will have an hourly rate, and hours worked Our program will need to create instances of each of our employee types to represent the employees our factory employees, and to calculate their pay. So each of our employee types will become object classes. Now, we can define our employee classes to contain all data members, both those specific to their subtype, and general to all employees (for simplicity we’ll focus on: Salaried Employee - FirstName: String - LastName: String - Title: String - HireDate: String Salary: float + Salaried() : [constructor] + Salaried(in first: String, in last: String, in date: String, in position: String, in Salary: float): [constructor] + Print(): void [query] + getFirst() : String [query] + setFirst(in first: String): void Hourly Employee - FirstName: String - LastName: String - Title: String - HireDate: String Hourly Rate: float Hours Worked: float Commission Employee - FirstName: String - LastName: String - Title: String - HireDate: String Salary: float comissionSales: float commissionRate: float + Hourly() : [constructor] + Commission() : [constructor] + Hourly(in first: String, in last: String, in date: String, in position: String, in rate:float, in hrsWorked: float): [constructor] + Comission(in first: String, in last: String, in date: String, in position: String, in base: float, in sales: float, in rate: float ): [constructor] + Print(): void [query] + Print(): void [query] + getFirst() : String [query] + getFirst() : String [query] + setFirst(in first: String): void + setFirst(in first: String): void There is a problem with this design choice, it duplicates code and decreases reliability and reusability. Because whenever the same code resides in more than one physical location and a change needs to be made to the algorithm the code contains, there will always be the possibility that the code will be modified in one location but not all. Whenever possible we want to avoid duplication of code. A different design choice would be to create one generic Employee object with contained all the possible data members needed by any subtype of employee. Employee - FirstName: String - LastName: String - Title: String - HireDate: String Salary: float comissionSales: float commissionRate: float Hourly Rate: float Hours Worked: float + Employee() : [constructor] + Employee(in first: String, in last: String, in date: String, in position: String, in salary: float, in sales: float, in rate: float, in rate: float, in hrsWorked: float ): [constructor] + Print(): void [query] + getFirst() : String [query] + setFirst(in first: String): void Again this is a poor design choice, when we design a class EVERY data member must be applicable to EVERY instance of the class, and every method should appropriate for every instance. For example if we have a method + calculateComission() : float which calculates a commission based employee’s commission based on sales, it SHOULD not be applied to an instance of another type of employee, but it could in this design. This design is not cohesive. So a better design choice is to define a “generic” Employee class that contains ONLY those data members that all subtypes of employees share in common. Employee - FirstName: String - LastName: String - Title: String - HireDate: String + Employee() : [constructor] + Employee(in first: String, in last: String, in date: String, in position: String): [constructor] + Print(): void [query] + getFirst() : String [query] + setFirst(in first: String): void Then we can use inheritance to define subclasses of class Employee that automatically obtain the shared features from employee, but then implement type specific features that represent only the individual subclass. For simplicity we will focus on only the subclasses Salaried Employee and hourly employee. Employee Salaried Employee Hourly Employee When we create a subclass, via the “extends” keyword, it automatically inherits or gets a copy of all of the public and private members of the parent class. So when class Salaried employee is defined, it will inherit the data members firstName, and lastName etc, however it will only be able to directly access the public members inherited from class Employee. Let’s work through an example. A partial implementation of class Employee is: public class private private private private Employee{ String firstName; String lastName; String title; String hireDate; public Employee(){ firstName = new String("unknown"); lastName = new String("unknown"); title = new String("unknown"); hireDate = new String("unknown"); }// end default constructor public Employee(String first, String last, String position, String date) { firstName = first; lastName = last; title = position; hireDate = date; }// end non default constructor public void setFirst(String name) { firstName = new String(name); } public String getFirst() { return firstName; } public void print() { // this method prints out the data stored in all // data members for a specific instance System.out.println("The employee is " + firstName + " " + lastName + " , and was hired on " + hireDate + " for the position of " + title); }// end print }// end employee We can write a test program to create employees and print out their information such as: public class testEmployee{ public static void main(String[] args){ Employee john = new Employee("John", "Adams", "Janitor", “2/18/2008”); Employee unknown = new Employee(); john.print(); // print the contents of John unknown.print(); // print the contents of unknown // change unknown’s first name, and print again unknown.setFirst("Sandy"); unknown.print(); }// main }// end testemployee When the test program is executed it produces the results The employee is John Adams , and was hired on 2/18/2008 for the position of Janitor The employee is unknown unknown , and was hired on unknown for the position of unknown The employee is Sandy unknown , and was hired on unknown for the position of unknown Overloaded methods: A class can contain more than one method by the same name. When this occurs the method is said to be overloaded. Each overloaded method must be distinguishable through the number and type of their parameters. When we have multiple constructors in a class, the constructors are overloaded. As an example of overloading a method, we can add a second “print” method to class Employee, which receives an additional string from the client program that is to be added to the end of the information printed. public void print(String xtra) { System.out.println("The employee is " + firstName + " " + lastName + " , and was hired on " + hireDate + " for the position of " + title + " \n The extra information to be printed is " + xtra); }// end print The java interpreter decides which version of print to execute based on parameters, return type, and the TYPE of the object to which the method is applied. We can test this in our test program as: public class testEmployee{ public static void main(String[] args){ Employee john = new Employee("John", "Adams","2/18/2008","Janitor"); Employee unknown = new Employee(); john.print(); unknown.print(); unknown.setFirst("Sandy"); unknown.print(); john.print("Camille"); }// main }// end testemployee Which produces the output: (differences are highlighted in red) The employee is John Adams , and was hired on 2/18/2008 for the position of Janitor The employee is unknown unknown , and was hired on unknown for the position of unknown The employee is Sandy unknown , and was hired on unknown for the position of unknown The employee is John Adams , and was hired on 2/18/2008 for the position of Janitor The extra information to be printed is Camille Subclasses: To define a subclass of an existing class, on the title line of the class we add the “extends” keyword and the name of the parent class: public class Salaried extends Employee{ private float salary; public Salaried() { salary = 0.0f; } public Salaried(String first, String last, String date, String position, float sal) { salary =sal; } }// end class Although class Salaried “inherits” the private data members of class Employee, they are still private, and thus hidden. This means that the constructors and methods inside Salaried can not directly reference these data members by name using the dot (.) operator. For example the following would be illegal in the default constructor for class Salaried: public Salaried() { firstName = new String("unknown"); lastName = new String("unknown"); title = new String("unknown"); hireDate = new String("unknown"); salary =sal; } When compiled this would produce the result: ÏSalaried.java:13: firstName has private access in Employee firstName = new String("unknown"); ^ Salaried.java:14: lastName has private access in Employee lastName = new String("unknown"); ^ Salaried.java:15: title has private access in Employee title = new String("unknown"); ^ Salaried.java:16: hireDate has private access in Employee hireDate = new String("unknown"); ^ 4 errors Private members of parent classes are still private, period. To correctly initialize the private data members of the parent class, you need to invoke a constructor from the parent class, and this is done via the Super keyword. The parent class of subclass is also referred to as the subclass’ “Super” class. To invoke the constructor you use the keyword super followed by parameters. IE: public Salaried() { super(); // calls the default constructor of employee salary = 0.0f; } public Salaried(String first, String last, String date, String position, float sal) { // calls the non-default constructor super(first, last, position, date); salary =sal; } The invocation of the parent class’s constructor must be the first line in the child class’s constructor. Once a child class is created, all public methods inherited from a parent class can be applied to an object of the child class in either a client program, or inside the body of a method in the child class. This is because inheritance in java is an example of pure subtype or “isa” inheritance. This means that in instance of the child class can be treated in all ways that an instance of the parent class can, a SalariedEmplyee isa Employee public class testEmployee{ public static void main(String[] args){ Employee john = new Employee("John", "Adams","Janitor", "2/18/2008"); Employee unknown = new Employee(); Salaried Pam = new Salaried("Pam", "Jones","7/1/2009”, “Surgeon”, 30000f); john.print(); unknown.print(); Pam.print(); }// main }// end testemployee This displays: The employee is John Adams , and was hired on 2/18/2008 for the position of Janitor The employee is unknown unknown , and was hired on unknown for the position of unknown The employee is Pam Jones , and was hired on 7/1/2009 for the position of Surgeon When the print() method was applied to object Pam, the type of Pam was determined, which is “Salaried”. Class Salaried is checked for an instance method named print(). If it exists it is executed, if not the super class of Salaried is checked, and the print() method from class Employee is executed, which does not print any of the data members specific to a salaried employee, such as Salary. Overridden method in child classes: A child class may re-implement or override any public method implemented in the parent class. For example, we can re-implement print() inside Salaried as: public void print() { System.out.println("Print method in Salaried"); System.out.println("The employee earns " + salary + " per year."); } This time when the print() method was applied to object Pam in our test program, the print() from class Salaried would be used and would display: The employee is John Adams , and was hired on 2/18/2008 for the position of Janitor The employee is unknown unknown , and was hired on unknown for the position of unknown Print method in Salaried The employee earns 30000.0 per year. This time the print method from Salaried is called, but only prints the individual’s salary. Again we can not directly access the data members inherited from Employee. If we tried the following: public void print() { System.out.println("Print method in Salaried"); System.out.println("The employee's name is " + this.firstName + " " + this.lastName + " and the employee earns " + salary + " per year."); } this generates the errors: Salaried.java:20: firstName has private access in Employee this.firstName + " " + this.lastName + ^ Salaried.java:20: lastName has private access in Employee this.firstName + " " + this.lastName + ^ To access the private members, we must either use accessors (getters) provided by the parent class, IE: public void print() { System.out.println("Print method in Salaried"); System.out.println("The employee's name is " + this.getFirst() + " and the employee earns " + salary + " per year."); } which would cause our test program to produce the output: The employee is John Adams , and was hired on 2/18/2008 for the position of Janitor The employee is unknown unknown , and was hired on unknown for the position of unknown Print method in Salaried The employee's name is Pam and the employee earns 30000.0 per year. Another alternative is that from inside the body of a method in a child class, an overridden method from the parent class can be invoked via the “super” keyword. IE: public void print() { System.out.println("Print method in Salaried"); super.print(); System.out.println("The employee's name is " + this.getFirst() + " and the employee earns " + salary + " per year."); } super.print(); Invokes the print() method from class Employee and after that method completes, execution resumes inside the print() method of class Salaried. Our test program will now produce: The employee is John Adams , and was hired on 2/18/2008 for the position of Janitor The employee is unknown unknown , and was hired on unknown for the position of unknown Print method in Salaried The employee is Pam Jones, and was hired on 7/1/2009 for the position of Surgeon The employee's name is Pam and the employee earns 30000.0 per year. Abstract Classes: Sometimes in the design of a hierarchy of related classes, it doesn’t make sense from the application standpoint to declare instances of the parent class. In our example, would there ever be just a generic “employee” who isn’t either salaried, hourly, or commissionbased? We can define our parent class so that is can only serve as a parent for subclasses, allowing no instances of it to be declared in a client program. The abstract keyword does this. public abstract class Employee{ private String firstName; private String lastName; private String title; private String hireDate; public Employee(){ firstName = new String("unknown"); lastName = new String("unknown"); title = new String("unknown"); hireDate = new String("unknown"); }// end default constructor public Employee(String first, String last, String position, String date) { firstName = first; lastName = last; title = position; hireDate = date; }// end non default constructor public void setFirst(String name) { firstName = new String(name); } public String getFirst() { return firstName; } public void print() { // this method prints out the data stored in all // data members for a specific instance System.out.println("The employee is " + firstName + " " + lastName + " , and was hired on " + hireDate + " for the position of " + title); }// end print }// end employee By making a class abstract, we are indicating that it can only serve as a parent for other classes. So in our test program: public class testEmployee{ public static void main(String[] args){ Employee john = new Employee("John", "Adams","Janitor", "2/18/2008"); Salaried Pam = new Salaried("Pam", "Jones", "7/1/2009","Surgeon", 30000f); john.print(); Pam.print(); }// main }// end testemployee This would generate the error: testEmployee.java:5: Employee is abstract; cannot be instantiated Employee john = new Employee("John", "Adams","Janitor", "2/18/2008"); An abstract class, can include the definition of both static and instance data members, and the implementation of public and private methods. Sometimes an abstract class resemble an interface by defining abstract methods. An abstract method defined within an abstract class, specifies an interface for a method, but does NOT implement the body. Any child class of the abstract class is then REQUIRED to implement the method. For example, the following abstract method might be implemented inside Employee. public abstract float calculatePay(); Any class, ie: Salaried, that extends Employee must implement a method with the same name, parameters, and return type. So now the implementation of Salaried must include: public float calculatePay() { // there are 24 pay periods in the year float pay = this.salary/24.0f; System.out.println("The employee will earn " + pay + " each pay period. "); return pay; } Our test program can be modified to: public class testEmployee{ public static void main(String[] args){ Salaried Pam = new Salaried("Pam", "Jones", "7/1/2009","Surgeon", 30000f); Pam.print(); Pam.calculatePay(); }// main }// end testemployee and will produce the result: Print method in Salaried The employee is Pam Jones , and was hired on 7/1/2009 for the position of Surgeon The employee's name is Pam and the employee earns 30000.0 per year. The employee will earn 1250.0 each pay period. Polymorphism Polymorphism is the definition of operations that apply to more than one type: Polymorphic means having many forms. In object oriented languages polymorphism refers to the late binding of a call to a method to the actual implementation of a specific method.. Polymorphic operations are defined for “classes” or “families” of types that have common properties… These common properties make it meaningful to define an operation applying to all sub-types in the class. For example it is possible to define an operation for a parent class which can be used or redefined by all subclasses of the parent. In our employee example method print() would be a polymorphic operation. Basically polymorphism works like this: as a program executes, an object will have an apparent and an actual data type. The apparent data type of an object is the data type of the variable to which it is assigned, which must be the same as or an ancestor class of the actual type of the object. When a method is applied to an instance of the object, dynamically at runtime the actual data type of the object is obtained, and the correct version of the overridden method is executed. As an example: if you have an object of a declared (or apparent) type T whose actual type may vary at run time.. T objA; A method call: objA.m(); the actual instance of the method m that is called depends on the actual or dynamic type of the object at runtime. Consider an application that implements a drawing program, such that in a program there are many different kinds of graphical objects such as circles, rectangles ..etc.. Each capable of displaying themselves in a window, using a “paint” method. Each type of graphic object belongs to a family of graphic objects called “graphicalObj Graphic Object: Color Pattern Paint Square: Color Pattern Length paint Circle: Color Pattern Radius paint Text: Color Pattern Font paint Instances of child classes, can be referred to as an instance of the parent class, if we have a collection of random graphic objects stored in a linked list: List of graphical Obj Text: Square: Circle: Text: If we iterate through this list: foreach (graphicalObj obj in list) { obj.paint(); .. } Each graphical object paints itself according to its own logic.. squares differently than circles.. so the actual paint method being called varies according to the actual type of the graphical object. Yet each is merely asked to paint itself in turn.. Each instance of the paint method must uphold the principle of substitutability.. each instance of the paint method performs the same abstract function, with only the code being particularized for the individual type or subtype. The profile and the function for each variation of paint must be identical In terms of our application, we can assign an object of type Salaried, in a variable of type Employee: public class testEmployee{ public static void main(String[] args){ Employee unknown = new Salaried("Larry", "Johnson", "6/1/2003", "Baker", 15000f); Salaried Pam = new Salaried("Pam", "Jones", "7/1/2009","Surgeon", 30000f); Pam.print(); Pam.calculatePay(); unknown.print(); }// main }// end testemployee This would produce the result: Print method in Salaried The employee is Pam Jones , and was hired on 7/1/2009 for the position of Surgeon The employee's name is Pam and the employee earns 30000.0 per year. The employee will earn 1250.0 each pay period. Print method in Salaried The employee is Larry Johnson , and was hired on 6/1/2003 for the position of Baker The employee's name is Larry and the employee earns 15000.0 per year. When an object of a child type is stored in a variable of the base (parent type) this is called upcasting. The object can be converted back to an object of the child type by casting it as you would a float, int or double. However, before casting you should ideally check to see if it is an object of the correct type though the instanceof operator public class testEmployee{ public static void main(String[] args){ Employee unknown; Salaried Pam = new Salaried("Pam", "Jones", "7/1/2009","Surgeon", 30000f); Pam.print(); Pam.calculatePay(); // the following statements would store a reference // the object contained in variable Pam into the variable // unknown. Applying the print method to object unknown // would invoke the print method for class Salaried. However // the apparent type of the object is “Employee” unknown = Pam; unknown.print(); // To store the object contained in “unknown” into a variable // of the correct type we must first validate that it is // of the correct type, before casting Pam = null; //destroys the address stored in Pam if (unknown instanceof Salaried) Pam = (Salaried) unknown; // this changes the apparent type of the object, and stores the // reference into pam }// main }// end testemployee Doing this type of a conversion is downcasting. Generics Beginning with Java 5.0, we can define special parameterized classes that contain a “parameter” for representing an unknown type. This is one way to write generalized ADT’s that can be easily reused in applications without the overhead of upcasting and downcasting. Previously when we wrote queues, stacks, and lists, we created them for integers, if we wanted to used them for some other data type we had to “rewrite” the code changing int to whatever… this violates the whole idea of reuse…ideally we should be able to use the class as is with minimal modification OR we defined their type to be object… Instead of coding the actual type of a data member, classes and methods can use a type parameter <T> to stand for some unknown type, and may then have any reference type (class type) plugged in for that parameter, when an instance of the class is created. (Note: you can use any non-keyword name for the parameter, not just T) The class definition is stored in a java file and compiled like any other class, but when it is used to declare a variable in a program the actual type to be plugged in for the generic parameter must be supplied: The type plugged in for a type parameter must be a reference type, it can not be a primitive type such as int or float, but we can use the wrapper classes Integer etc. public class gClass<T> { private T data; public void setData(T newData){ data = newData; } public T getData() { return data; } } A sample test program for this class would be: public class testgClass{ public static void main(String[] args) { gClass<String> name = new gClass<String>(); gClass<Integer> anInt = new gClass<Integer>(); name.setData("Camille Hayhurst"); System.out.println("the contents of name is " + name.getData()); anInt.setData(55); System.out.println("The value of anInt minus 20 is (anInt.getData() -20)); } } " + Interfaces Interfaces in Java, are NOT classes. If you go to the Java API, they appear in the “classes” window in italics. Since they are not classes, they can not be used to declare variables and we can not create instances of them. They exist to enhance interoperability between the existing classes in the Java API, and user defined ADTs. They are templates, that specify behaviors in terms of methods to implement in user defined ADTs. These behaviors help user defined classes to work with the classes in the Java API. A commonly used interface is the “Comparable” interface. This interface specifies a “compareTo” method that a class can implement so that it can work with any API class that orders objects according to their natural ordering. For example classes String, Integer, Float, and Character all implement the Comparable interface, and implement a compareTo method. You are already familiar with the compareTo method for class String. Assuming that there exists two strings wordA and wordB wordA.compareTo(wordB) returns 0 if they contain the same string, a value <0 if wordA is lexicographically less than wordB, and a value >0 if wordA is greater than wordB. Access the java API, and open the Comparable interface This interfaces specifies the heading for a compareTo method in the following form public int compareTo( Object B) The type of the parameter must be Object. Assume that the following class exists: To indicate that YOUR class adheres to the behaviors specified in an interface, you add the keyword implements to the first line in your class, and then name the interface(s) you will implement. Then in the body of your class you must include a method for each specified in the interface. public class MyInteger implements java.lang.Comparable{ private int number; public MyInteger( int value){ number = value; }// end constructor } To implement a compareTo method for this class I would add the following code: public int compareTo( Object B) { // to compare two objects, they must be of the same type, so we must cast the // parameter to the actual type needed, which is the type of the class MyInteger objB = (MyInteger) B; if ( this.number == objB.number) return 0; else if (this.number > objB.number) return 1; else return -1; }// end compareTo The following is a main program that uses class MyInteger: public class testMyInteger{ public static void main(String[] args){ MyInteger X = new MyInteger(5); MyInteger Y = new MyInteger(5); MyInteger Z = new MyInteger(9); System.out.println("X.compareTo(Y) equals " + X.compareTo(Y)); System.out.println("Y.compareTo(Z) equals "+ Y.compareTo(Z)); System.out.println("Z.compareTo(X) equals " + Z.compareTo(X)); } }// end class Displays the output: X.compareTo(Y) equals 0 Y.compareTo(Z) equals -1 Z.compareTo(X) equals 1 But this still doesn’t solve our problem, because class Object does not implement the Comparable interface. SOOOO.. Instead of using Object, we can specify “Comparable” as the data type of our elements. This will ENFORCE that any object inserted into our list by the client program MUST be of a class type that implements the Comparable interface.