cs445 - Fall 2016

Homework #1 - Inheritance, Abstraction, and Polymorphism

Goal

This purpose of this homework is to provide you with experience developing classes using inheritance. You will use abstract classes, concrete classes, and interfaces to write a simple program that uses polymorphism.

Overview

  • Understand and use interfaces
  • Understand and use inheritance hierarchies
  • Understand and use abstract classes
  • Understand and use interfaces with inheritance mechanisms
  • Create a hierarchy of classes
  • Learn how to use the Object.toString() method
  • Use polymorphism in a simple program

1. Creating a class hierarchy [ 20% ]

You will develop a program that creates a class hierarchy of Things and Creatures.

Click here to see the Javadoc pages that describe these classes. Read this documentation so that you understand what each class does.

First write a test program called TestCreature to test that the class hierarchy works correctly. The TestCreature class contains the main method. The main() must create an array of Thing instances of size THING_COUNT; see javadocs for specifics.

Please note that TestCreature is what one would call an "acceptance test". Just because you're writing this test doesn't mean you don't have to write your regular unit testing using jUnit.

Create the array and fill it with Thing instances (any non-empty name will do). Next, main() must scan through the array and print the String representation of each Thing. Note that you do not need to call the toString() method on each object; just print each object reference from the array directly.

The class Thing is a concrete superclass in the class hierarchy. A client program, such as your test program, can create instances of the class and print them.

The class Creature is an abstract superclass in the class hierarchy. An abstract class represents generalization: a general concept embodied by one or more specific cases. It is illegal to create an instance of an abstract class.

A concrete subclass of a generalization represents a specialization: a specific case of some general concept. The idea of inheritance depends on chains of generalizations and specializations.

Read the documentation for the Thing and Creature classes. Notice that some of the methods have a declaration, and they have no method body. Methods declared but not implemented are abstract methods, which all concrete subclasses must implement.

Read the javadoc comments for the toString() method for Thing. The method must create a String that contains the name of the Thing, followed optionally by the class name. Since every Thing instance has a name, that should be a private data member of Thing. The class name of a Thing instance, however, is not a property of the class; it is a unique property of all of Thing's subclasses (and their subclasses).

When you store a reference to a Tiger (for example) in a variable of type Thing, the specific class may be difficult to see. How should you deal with getting the class name of an object reference?

One naive and tempting route to take is to use a series of instanceof checks on the subclass instance that invokes Thing.toString().


   String type;
   if ( this instanceOf Creature ) {
       // deal with Creatures here; this is a Creature!
   } else if ( this instanceof SomethingElse ) {
       // deal with SomethingElse here
   } // ...

This chain of instanceof checks is a very poor design practice, because it requires constant maintenance of the Thing class whenever a new subclass is introduced. The paragraphs below describe a better way.

Since all classes inherit from Object, each class automatically has access to Object.getClass() , which returns a reference to the instance's class instance. With a reference to the Class, get the class name using Class.getSimpleName() . These two calls together will always produce the class name of the instance that is running the method:


   // ...
   String className = getClass().getSimpleName();
   // ...

After you have written Thing, write Creature, which is an abstract subclass of Thing. Be sure to use the constructor provided by the Thing class when writing your Creature class by calling the super constructor appropriately.

Printing a Creature or any of its subclasses, should use Thing.toString(); there is no need to implement toString as a method in Creature.

Note: a Creature. needs to remember only the last thing it eats.

After you have written these classes, compile them and your test program to make sure that they compile without error. Then run these using your test program.

Now write the Tiger class to inherit from the hierarchy. Remember: If a subclass can use a method implementation of a superclass as-is, there is no need to re-write the method. If the method is not private, a client can access it.

Please use a stubbing approach to write the abstract methods of Tiger. A stub method does nothing except implement a method signature and ensure that the class compiles.

Finally, revise the TestCreature program to create and print a few Tiger instances. Add code to the main() to create several instances of Tiger in the Thing array; this is in addition to the earlier Thing instances. As a result some Thing instances will be Things and others will be Tigers. Make sure that all classes compile without error, and re-run your test program to validate it.


2. Introducing Interfaces for implementing Additional Behavior [ 40% ]

This activity expands the Thing and Creature framework by adding more creatures and giving them different behaviors.

The next job is to write three more classes, Ant and Fly and Bat that inherit from Creature. You must not modify the Thing.toString() method. Start by reading the javadocs for each class.

Please use a stubbing approach to write a stub for the abstract methods. As described above, a stub method does nothing except implement a method signature and ensure that the class compiles.

A Java interface can achieve results similar to multiple inheritance because a single class may implement more than one interface.

Implement an interface called Flyer that specifies the fly() method.

Modify your test program to test the Creatures. The main() must create a separate array for Creature instances. Add a new section of code to create several new Creature instances. Consequently, the main() will have a 'Things:' section followed by a 'Creatures:' section of output. The section for the creatures must simply print the instances (the next activity will revise that).

Below is an example of output for this activity. The 'Creatures:' section evolves in the next section.


Things: 

Banana
Tigger, Pooh's Friend
Locomotive
Tick-Tock the Crocodile

Creatures: 

Tigger, Pooh's Friend
Tick-Tock the Crocodile


3. Completing the Thing-Creature Framework [ 40% ]

This part of the assignment completes the program by finishing the stubbed methods the subclasses of Things and Creatures. This means implementing the Creature.eat(), Creature.move(), and Creature.whatDidYouEat() for all the concrete subclasses.

The javadocs describe restrictions on the eating diets of each creature. Read carefully to learn which creatures eat only things, which eat only creatures, and which eat anything.

This activity also requires evolving the TestCreature program to call these new methods and validate their operation. The 'Creatures:' section of TestCreature must now test the methods just implemented in the concrete classes. TestCreature now must have a separate Creature[] array containing references on which to iterate and call the move() method on the different kinds of Creature instances. Note that creatures that move by flying should be using their fly method.

Each class's methods must implement behavior by printing a message to standard output. This message is a simple illustration of different behavior provided by the same message (that is, the same method call on different objects produces different results). This is polymorphism.

Reminder: To invoke a method of a superclass with the same name from the subclass, prefix the method name in the call with super., as in super.toString().


4. Varia

Follow the Assignment Submission instructions in the syllabus to submit your work. Don't forget that we'll also have a look at your code repository, make sure you've shared it with us and that you also include instructions for how to check out your code from the command line and how to build it.

Don't forget the check out the syllabus for the test environment of your work and what's needed in terms of automated unit testing.



$Id: hw1.html,v 1.1 2016/08/28 15:05:40 virgil Exp $