Universidad Carlos III de Madrid

Ingeniería de Telecomunicación

Enero-Mayo 2010 / January-May 2010

Object-Orientation & Inheritance

Lab Section1.  Session 4 (lab): Object-Orientation & Inheritance (II)

Exercise Section1.1.  Class Hierarchies and Polymorphism

You have been asked to code a Windows application to manage the users of the UC3M swimming pool.

The application should be a very simple database system, which would store the data of all the swimming pool users. It must be capable of listing all the users and their data.

There are 5 types of swimming pool users:

  1. Outsider: people not related to the UC3M, e.g. people living in Leganés. The only datum we know from them is their DNI (the Spanish national identity card).

  2. Staff: Office workers and support people on the campus, but not teachers. We know their DNI and their salary.

  3. Professor: UC3M teachers. We know their DNI, their department and their salary.

  4. Student: We know their DNI and their NIA (Student identification number).

  5. Intern: Regular students that are also working for the university. We know all their student data and also their salary.

Section 1. Class Hierarchy

Which of the following diagrams best represent the class hierarchy of the application? Discuss the pros and cons of all diagrams with a classmate for 15 minutes.

Solution

There is no right and wrong answer to this exercise, but it raises some good points.

Diagram A is too simple, as it does not make use of a class hierarchy: there are a lot of code redundancies, for instance, the handling of some class attributes like dni and salary must be done in many classes.

On top of that, some of the dependencies between classes in the exercise are not properly represented in the diagram, for example, Interns should also be Students.

Diagram B is better, as it makes uses of class hierarchies, but it has some problems:

  1. People form Staff cannot be Professors, as the exercise explains.

  2. There is still too much code redundancy, for instance the dni and salary attributes are present in too many classes.

In diagram C, the class Outsider has been substituted by Person, a much more generic class. This allows eliminating the code redundancies for the handling of the dni attribute. The downside of this approach is that outsiders have now to be instances of the Person class; therefore some of the semantics of the exercise are lost. This can lead to problems in the future, if we want to modify the application or add more types of users.

And there is still the problem of Professors that are also Staff, which is not allowed by the exercise, and the code redundancy in the salary attribute.

Diagram D is very nice, there is no code redundancy on the dni attribute and Professors are no longer part of the Staff. There are still some code redundancies for the salary attribute, though.

In Diagram E, the problem of the code redundancy on the salary attribute is fixed by using the new superclass Worker. Sadly, Java does not allow inheriting from more than one class at the same time, thus this approach is impossible to implement. In future lessons, you will learn about Java Interfaces, a nice way to implement multiple inheritance in Java. But, for now, this approach is not possible.

Long story short: Diagram D is the preferred solution, even if we have to cope with some code redundancy for the salary, because, at the time being, we cannot do better.

Section 2. Polymorphism

Implement the application using diagram D as a reference. Code all the classes. Each class must declare all its attributes, a constructor and also a method String toString() to generate a textual representation of the known data of each user, according to the format below.

You must also code a test class, with only the main method. It should instantiate objects representing each user (listed below) to test the application. You must store all the user objects in a variable that can hold several Person objects (any Java collection will do, e.g. an ArrayList<Person>...). Then go through all the users in the collection, printing all their data to the standard output.

The expected behavior of your application must be like this (the order in which the users are printed is not relevant):

C:\Users\Alberto>java DataBase
DNI: 01100000-A
DNI: 00220000-B
DNI: 00030000-C, salary: 2000
DNI: 04040000-D, salary: 1500
DNI: 50500000-E, salary: 1000, department: mathematics
DNI: 66600000-F, salary: 2000, department: telematics
DNI: 77000000-G, NIA: 777777
DNI: 88080000-H, NIA: 888888
DNI: 90990000-I, NIA: 999999, salary: 400
DNI: 10100000-J, NIA: 101010, salary: 800

Solution

#### Person.java #####################
public class Person {
    private String dni;

    public Person(String dni) {
        this.dni = dni;
    }

    public String toString() {
        return "DNI: " + dni;
    }
}

#### Staff.java ######################
public class Staff extends Person {
    private int salary;

    public Staff(String dni, int salary) {
        super(dni);
        this.salary = salary;
    }

    public String toString() {
        return super.toString() + ", salary: " + salary;
    }
}

#### Professor.java ####################
public class Professor extends Staff {
    private String department;

    public Professor(String dni, int salary, String department) {
        super(dni, salary);
        this.department = department;
    }

    public String toString() {
        return super.toString() + ", department: " + department;
    }
}

#### Student.java ####################
public class Student extends Person {
    private String nia;

    public Student(String dni, String nia) {
        super(dni);
        this.nia = nia;
    }

    public String toString() {
        return super.toString() + ", NIA: " + nia;
    }
}

#### Intern.java #####################
public class Intern extends Student {

    private int salary;

    public Intern(String dni, String nia, int salary) {
        super(dni, nia);
        this.salary = salary;
    }

    public String toString() {
        return super.toString() + ", salary: " + salary;
    }
}

#### DataBase.java ###################
import java.util.ArrayList;

public class DataBase {

    public static void main(String args[]) {

        // All users are stored in the same collection, any Java
        // collection will do for this simple exercise
        ArrayList<Person> users = new ArrayList<Person>();

        // Fill the database with all users' data
        {
            Person p = new Person("01100000-A");
            users.add(p);
            p = new Person("00220000-B");
            users.add(p);

            Staff s = new Staff("00030000-C", 2000);
            users.add(s);
            s = new Staff("04040000-D", 1500);
            users.add(s);

            Professor f = new Professor("50500000-E", 1000, "mathematics");
            users.add(f);
            f = new Professor("66600000-F", 2000, "telematics");
            users.add(f);

            Student st = new Student("77000000-G", "777777");
            users.add(st);
            st = new Student("88080000-H", "888888");
            users.add(st);

            Intern in = new Intern("90990000-I", "999999", 400);
            users.add(in);
            in = new Intern("10100000-J", "101010", 800);
            users.add(in);
        }

        // Print all users
        for (int i=0; i<users.size(); i++) {
            Person p = users.get(i);
            System.out.println(p);
        }
    }
}

Section 3.

  1. Discuss with a classmate how is it possible to print all user data (data from Staff, Student... objects) if all of them are handled through references to a simple Person object.

  2. Discuss with a classmate why do you need to declare the String toString() as a public method on Person and all its subclasses. Would it be possible to declare that method package-private instead, for example?

Solution

  1. In Java, overridden method resolution is performed by virtual method invocation. This means that Java will resolve a method by looking at the class of the actual object and ignoring the class of the reference being used to handle the object.

  2. In Java, a method signature includes the access modifiers, therefore, when you want to override a method, you must keep its original access modifiers.

Section 4. Last minute changes

Just before submitting your application, the UC3M asks you to implement 2 extra functionalities:

  • Add a new user type Tenured. They are like standard Professors but have a fixed salary of 2500 Euros. This means that the class constructor should not have a salary argument. Also add these two instances of tenured professors to the database:

    DNI: 11110000-K, salary: 2500, department: geography
    DNI: 12120000-L, salary: 2500, department: mathematics
  • The application must support a command line argument "-s" for generating "short" listings, this is, only the basic data from the users must be printed (only the data available from the class Person).

The expected behavior of your application must be like this:

; java DataBase
DNI: 01100000-A
DNI: 00220000-B
DNI: 00030000-C, salary: 2000
DNI: 04040000-D, salary: 1500
DNI: 50500000-E, salary: 1000, department: mathematics
DNI: 66600000-F, salary: 2000, department: telematics
DNI: 77000000-G, NIA: 777777
DNI: 88080000-H, NIA: 888888
DNI: 90990000-I, NIA: 999999, salary: 400
DNI: 10100000-J, NIA: 101010, salary: 800
DNI: 11110000-K, salary: 2500, department: geography
DNI: 12120000-L, salary: 2500, department: mathematics
; java DataBase -s
DNI: 01100000-A
DNI: 00220000-B
DNI: 00030000-C
DNI: 04040000-D
DNI: 50500000-E
DNI: 66600000-F
DNI: 77000000-G
DNI: 88080000-H
DNI: 90990000-I
DNI: 10100000-J
DNI: 11110000-K
DNI: 12120000-L
  1. Estimate how long it will take you to implement each of these new functionalities, in hours or minutes.

  2. Implement these two functionalities in a new version of your application and compare the time it took you with your original estimation.

Solution

Adding the new Tenured class is simple: just inherit form Professor, define a class constant for the fixed salary and define a constructor using that constant in its call to the constructor of the superclass. It should take you about 5 or 10 minutes. Please note that you should not override the String toString() in this class.

Detecting the command line argument is also simple. Add the necessary logic to process the command line arguments, detect the usage of "-s" and also add some error checking for invalid arguments.

The fastest way to implement the short listing mode of operation is to add a new method to Person to generate a short listing of a user. As all other user classes are subclasses of Person they will automatically get this new functionality. This should take you between 10 and 15 minutes.

#### Tenured.java #####################
public class Tenured extends Professor {
    private static final int TENURED_SALARY = 2500;

    public Tenured(String dni, String department) {
        super(dni, TENURED_SALARY, department);
    }
}

#### Person.java #####################
public class Person {
    private String dni;

    public Person(String dni) {
        this.dni = dni;
    }

    public String toString() {
        return "DNI: " + dni;
    }

    public String toStringShort() {
        // return toString();       -- WRONG
        // return this.toString();  -- WRONG
        //
        // Sadly, none of these will work, as virtual method invocation
        // for subclasses will use their own subclass.toString() method,
        // hence code redundancy is unavoilable here
        return "DNI: " + dni; 
    }
}

#### DataBase.java ###################
import java.util.ArrayList;

public class DataBase {

    public static void main(String args[]) {

        // By default, listings are in long format
        boolean shortListingDesired = false;

        // Parse command args
        if (args.length > 1) {
            System.err.println("Invalid number of arguments");
            return;
        } else if (args.length == 1) {
            if (args[0].equals("-s")) {
                shortListingDesired = true;
            } else {
                System.err.println("Invalid argument: " + args[0]);
                return;
            }
        }

        // All users are stored in the same collection, any Java
        // collection will do for this simple exercise
        ArrayList<Person> users = new ArrayList<Person>();

        // Fill the database with all users' data
        {
            Person p = new Person("01100000-A");
            users.add(p);
            p = new Person("00220000-B");
            users.add(p);

            Staff s = new Staff("00030000-C", 2000);
            users.add(s);
            s = new Staff("04040000-D", 1500);
            users.add(s);

            Professor f = new Professor("50500000-E", 1000, "mathematics");
            users.add(f);
            f = new Professor("66600000-F", 2000, "telematics");
            users.add(f);

            Student st = new Student("77000000-G", "777777");
            users.add(st);
            st = new Student("88080000-H", "888888");
            users.add(st);

            Intern in = new Intern("90990000-I", "999999", 400);
            users.add(in);
            in = new Intern("10100000-J", "101010", 800);
            users.add(in);

            Tenured t = new Tenured("11110000-K", "geography");
            users.add(t);
            t = new Tenured("12120000-L", "mathematics");
            users.add(t);
        }

        // Print all users
        for (int i=0; i<users.size(); i++) {
            Person p = users.get(i);
            if (shortListingDesired)
                System.out.println(p.toStringShort());
            else
                System.out.println(p);
        }
    }
}