CPSC 115L: Introduction to Computing Fall2010

Lab 11: The Menagerie

November 17, 18

As usual, you are expected to work with an assigned partner as a pair. Both you and your partner will receive the same grade. Both of you should always save your laboratory work on your own accounts.

Objectives

The main objectives of this assignment are
  1. to give you practice writing a simple Java program using an object oriented paradigm.
  2. to give practive using inheritance and polymorphism in the design of a Java program.

Introduction

This lab will be mostly about object-oriented design and will focus on some of the desirable features of object-oriented paradigm. We will see how superclasses and inheritance can be used to simplify the design of program.

We will deal with Java language details and the differences between Java and Python in the context of an example.

The Dog Class

Suppose you are asked to design an application for a farm business and part of that project is to keep track of the various animals that one finds on the farm, including dogs, cats, cows, pigs, sheep, etc. As a starting point, you are told that, among other things, you need to keep track of the following attributes for each animal:

You begin by defining a Dog class as follows:

public class Dog {

    private String breed;
    private char gender;
    private float height;
    private float weight;
    private int age;

    public Dog() {
    }

    public Dog(String b, char g, float h, float w, int a) {
	breed = b;
	gender = g;
	height = h;
	weight = w;
	age = a;
    }

    public String getBreed() {
	return breed;
    }
    public void setBreed(String breed) {
	this.breed = breed;
    }

    public char getGender() {
	return gender;
    }
    public void setGender(char gender) {
	this.gender = gender;
    }

    public float  getHeight() {
	return height;
    }
    public void setHeight(float height) {
	this.height = height;
    }

    public float  getWeight() {
	return weight;
    }
    public void setWeight(float weight) {
	this.weight = weight;
    }

    public int  getAge() {
	return age;
    }
    public void setAge(int age) {
	this.age = age;
    }

    public String toString() {
	return "Dog: [" + breed + "," + gender + "," + height + ","  + weight + "," + age + "]";
    }
    
    public static void main(String args[]){
	Dog collie = new Dog("Collie", 'F', 2, 50, 2);
	System.out.println(collie);
	Dog greatDane = new Dog("Great Dane", 'M', 4, 100, 5);
	System.out.println(greatDane);
    }
}

Some important points about this as a typical Java class definition:

Running the Dog Program

In this lab we are going to use the Linux command-line. Open a terminal window. Change into your cpsc115 directory (assuming it is a subdirectory of your home directory) and create a directory for this lab and then change into that directory:
$ cd cpsc115
$ mkdir lab11
$ cd lab11
Download the Dog.java file and save it in your lab11 directory. To make sure you have downloaded it properly, list the files in your lab11 directory:
$ ls -l 
-rw-r--r--  1 rmorelli  rmorelli    791 Nov 16 06:30 Dog.java

To run this program, you first have to compile it using the javac program. This will translate the whole file, Dog.java, into a Java byte code file named Dog.class.

$ javac Dog.java
$ ls -l
-rw-r--r--  1 rmorelli  rmorelli    635 Nov 16 07:20 Dog.class
-rw-r--r--  1 rmorelli  rmorelli    791 Nov 16 06:30 Dog.java

Once you have compiled it, you can now run it using the java command. Here's what should happen:

$ java Dog
Dog [Collie,F,2.0,50.0,2]
Dog [Great Dane,M,4.0,100.0,5]

The Cat Class

Now that we have defined the Dog class, we can use it as a template for the other animal classes that we need to create. For example, if we want to define a Cat class, we can copy and paste the Dog class and replace all occurrences of Dog with Cat. Most of the definitions in the Cat class will remain exactly the same. Here is all we would have to change:

public class Cat {
    ...
    public Cat() {
    }

    public Cat(String b, char g, float h, float w, int a) {
    ...
    }

    ...

    public String toString() {
	return "Cat: [" + breed + "," + gender + "," + height + ","  + weight + "," + age + "]";
    }
    
    public static void main(String args[]){
	Cat tabby = new Cat("Tabby", 'F', 2, 50, 2);
	System.out.println(tabby);
	Cat calico = new Cat("Calico", 'M', 4, 100, 5);
	System.out.println(calico);
    }
}

So let's do that. Download the following definition of the Cat.java into your cpsc115 directory. And then compile and run it:

$ javac Cat.java
$ java Cat
Cat [Tabby,F,2.0,50.0,2]
Cat [Calico,M,4.0,100.0,5]

What's Wrong With This Approach

You can see that we could use this copy-and-paste approach to define all of the other animal classes we need for this application -- i.e., goats, pigs, cows, sheep, etc. But here's the problem. Suppose after we define 20-30 different animal classes, we decide that we forgot to include an important attribute -- e.g., the animal's birth date. To fix this we would have to revise every class definition we wrote. This is very tedious and error prone -- it would be difficult to keep track of whether we made the necessary changes in all the files.

And this revision process never ends -- programmers and software developers are always revising and updating their code.

Using Inheritance to Design the Menagerie

A far better approach would be to take advantage of Java's (and Python's) inheritance mechanism by organizing our animal classes into a hierarchy. The superclass for this hierarchy will be the Animal class, which will define the attributes and methods that are common to dogs, cats, and all other animals. These attributes will be inherited by the Dog and Cat classes. If we need to define additional attributes that are particular for cats or dogs, we can do so in the Dog or Cat subclass.

So by our new design, the Animal class would be defined as follows. Notice that we have added a new attribute type to distinguish what type of animal (dog, cat, etc.) and notice how type is used throughtout the class definition.

public class Animal {

    private String type;
    private String breed;
    private char gender;
    private float height;
    private float weight;
    private int age;

    public Animal() {
    }

    public Animal(String t, String b, char g, float h, float w, int a) {
	type = t;
	breed = b;
	gender = g;
	height = h;
	weight = w;
	age = a;
    }

    public String getType() {
	return type;
    }
    public void setType(String type) {
	this.type = type;
    }
    public String getBreed() {
	return breed;
    }
    public void setBreed(String breed) {
	this.breed = breed;
    }

    public char getGender() {
	return gender;
    }
    public void setGender(char gender) {
	this.gender = gender;
    }

    public float  getHeight() {
	return height;
    }
    public void setHeight(float height) {
	this.height = height;
    }

    public float  getWeight() {
	return weight;
    }
    public void setWeight(float weight) {
	this.weight = weight;
    }

    public int  getAge() {
	return age;
    }
    public void setAge(int age) {
	this.age = age;
    }

    public String toString() {
	return type + " [" + breed + "," + gender + "," + height + ","  + weight + "," + age + "]";
    }
}

TODO: Download Animal.java and save it in your lab11 folder. Given this defintion of Animal, we can now define Dog and Cat very simply as follows:
public class Dog extends Animal {

    public Dog() {
    }

    public Dog(String b, char g, float h, float w, int a) {
        super("Dog", b, g, h,w,a);      // Invoke the Animal constructor
    }

    public static void main(String args[]){
	Dog collie = new Dog("Collie", 'F', 2, 50, 2);
	System.out.println(collie);
	Dog greatDane = new Dog("Great Dane", 'M', 4, 100, 5);
	System.out.println(greatDane);
    }
}
public class Cat extends Animal {

    public Cat() {
    }

    public Cat(String b, char g, float h, float w, int a) {
        super("Cat", b, g, h,w,a);      // Invoke the Animal constructor
    }

    public static void main(String args[]){
	Cat tabby = new Cat("Tabby", 'F', 2, 50, 2);
	System.out.println(tabby);
	Cat calico = new Cat("Calico", 'M', 4, 100, 5);
	System.out.println(calico);
    }
}

There are several important points about these subclass definitions.

TODO: Download both Dog.java and Cat.java and save them in your lab11 folder. NOTE: These will replace the previous versions of Dog.java and Cat.java. So if you want to save those you will have to rename them OldDog.java and OldCat.java.

Compile and run the Dog.java and Cat.java programs. You will see that they run just as before -- we have changed the design of this program but not its functionality.

$ javac Dog.java 
$ java Dog
Dog [Collie,F,2.0,50.0,2]
Dog [Great Dane,M,4.0,100.0,5]
$ javac Cat.java
$ java Cat
Cat [Tabby,F,2.0,50.0,2]
Cat [Calico,M,4.0,100.0,5]

Note that when you compile Dog.java or Cat.java, javac will automatically compile Animal.java and any other classes you need:

-rw-r--r--   1 ram  ram   2020 Nov 17 07:54 Animal.class
-rw-r--r--   1 ram  ram   1200 Nov 17 08:47 Animal.java
-rw-r--r--   1 ram  ram    630 Nov 17 08:28 Cat.class
-rw-r--r--   1 ram  ram    405 Nov 17 08:22 Cat.java
-rw-r--r--   1 ram  ram    418 Nov 17 08:54 Dog.class
-rw-r--r--   1 ram  ram    418 Nov 17 08:54 Dog.java

Exercises

For each of the following exercises, copy and paste your outputs into a file named lab11.out that you will hand in at the end of lab.

  1. The main() method. The main() method is an optional part of the class definition. It can be placed in any class. Define a main() method for the Animal.java class by copying and pasting all the statements from the main() methods in Dog.java and Cat.java. Compile and run Animal.java. Your output should look something like this:
    $ javac Animal.java
    $ java Animal
    Dog [Collie,F,2.0,50.0,2]
    Dog [Great Dane,M,4.0,100.0,5]
    Cat [Tabby,F,2.0,50.0,2]
    Cat [Calico,M,4.0,100.0,5]
    
  2. A Driver Class. It is often the case that the main() method is put in a separate class, sometimes called the driver program. Define a class named TestAnimals and save it in a file named TestAnimals.java. This class will have no attributes and no methods. Copy your main() method from the previous exercise into this class. Now compile and run this class. You should get the same output as before:
    $ javac TestAnimals.java
    $ java TestAnimals
    Dog [Collie,F,2.0,50.0,2]
    Dog [Great Dane,M,4.0,100.0,5]
    Cat [Tabby,F,2.0,50.0,2]
    Cat [Calico,M,4.0,100.0,5]
    
  3. Invoking Public Methods. Once you have created a Dog or a Cat instance, you can invoke public accessor or mutator methods on that instance. Write a series of System.out.println() statements to display the breeds and genders of all your Dog and Cat instances. Here's an example:
      Dog collie = new Dog("Collie", 'F', 2, 50, 2);
      System.out.println("I am a " + collie.getBreed() + " and I am " + collie.getGender());
    
  4. Invoking Public Methods. Repeat the previous exercise but this time with a mutator method. Add 1 year to the age of each of your animals. Here's an example:
      Dog collie = new Dog("Collie", 'F', 2, 50, 2);
      System.out.println(collie);
      collie.setAge( collie.getAge() + 1 );
      System.out.println("I am now " + collie.getAge() + " years old.");
    
  5. Adding a Name Attribute. We should give our animals names. Modify the Animal to add a name attribute. Here's a list of the things you have to change:
    • In Animal define the name attribute and the getName() and setName() methods.
    • In Animal revise the Animal() constructor method to accept a name parameter.
    • In Animal revise the toString() method to display the name.
    • In Animal comment-out the main() method.
    • In Dog revise the constructor and comment-out the main() method.
    • In Cat revise the constructor and comment-out the main() method.
    • In TestAnimals revise the statements you used to create Dog and Cat to use the new constructors you defined.
  6. Extending the Animal HierarchyDefine a new class for Cow. Model it after the current version of Dog or Cat. Add statements to TestAnimals to create and display your Cow instances.
  7. Chasing Animals. Add a method named chase() to the Animal class. This method should take one parameter, an Animal and it should just print a message reporting that this animal (the animal it is invoked on) is chasing another animal. Like in Python, an object can refer to itself using the this keyword (instead of self). Here is a stub definition of the chase() method:
    public void chase(Animal other) {
        // Print that this name is chasing other.
    }
    

    When you recompile and run Animal, it should print something like this:

    Dog [Fido,Collie,F,2.0,50.0,3] is chasing Dog [Big guy,Great Dane,M,4.0,100.0,5]
    

Documentation

Place a comment block at the beginning of each of your files: Cow, Animal, TestAnimals, using the javadoc format -- e.g.,
/**
 *  File: Animal.java
 *  Name: Your name
 */

What to hand in

Hand in your source code for each of your files and a copy of your outputs.


* CPSC 115L home page
Valid HTML 4.01!