Algorithms and OOD (CSC 207 2014F) : Labs

Laboratory: Inheritance


Summary: In today's laboratory, you will explore inheritance in Java by building and extending some simple classes.

Preparation

Create an Eclipse Java project for this lab and a Java package in that project. (I'd recommend that you also create a Git repository, but it's up to you.)

Exercises

Exercise 1: Your Base Class

Write a class, Counter, in package username.util. The class will allow clients to build objects that count things, starting at some value.

The class should contain

  • Two int fields, count and start.
  • One constructor that takes a starting value as a parameter. The constructor should initialize both count and start to that value.
  • Four methods:
    • increment(), which adds 1 to count (note that increment may throw an exception);
    • reset(), which resets count to start;
    • toString(), which returns a string of the form "[" + this.count + "]".
    • value(), which returns the value of count.

Here is a simple, not so systematic, test for that class.

  @Test
  public void test() throws Exception 
  {
    Counter alpha = new Counter(0);
    Counter beta = new Counter(123);
    Counter gamma = new Counter(-5);
    assertEquals("original alpha", 0, alpha.value());
    assertEquals("original beta", 123, beta.value());
    assertEquals("original gamma", -5, gamma.value());
    for (int i = 0; i < 10; i++) 
      {
        alpha.increment();
        beta.increment();
        gamma.increment();
      } // for
    assertEquals("updated alpha", 10, alpha.value());
    assertEquals("updated beta", 133, beta.value());
    assertEquals("updated gamma", 5, gamma.value());
    alpha.reset();
    beta.reset();
    gamma.reset();
    assertEquals("reset alpha", 0, alpha.value());
    assertEquals("reset beta", 123, beta.value());
    assertEquals("reset gamma", -5, gamma.value());
  } // test()

And here is an equally simple experiment.

import java.io.PrintWriter

/**
 * A simple experiment to allow us to explore our counter class.
 */
public class CounterExpt 
{
  public static void main(String[] args) 
  {
    // Set up output
    PrintWriter pen = new PrintWriter(System.out, true);

    // Set up some counters
    Counter alpha = new Counter(0);
    Counter beta = new Counter(123);
    Counter gamma = new Counter(-5);

    // Print original values
    pen.println("Original alpha = " + alpha);
    pen.println("Original beta = " + beta);
    pen.println("Original gamma = " + gamma);

    // Print incremented values
    alpha.increment();
    beta.increment();
    gamma.increment();
    pen.println("Updated alpha = " + alpha);
    pen.println("Updated beta = " + beta);
    pen.println("Updated gamma = " + gamma);

    // And we're done
    pen.close();
  } // main(String[])
} // class CounterExpt

Exercise 2: Tallys

One of the key ideas of inheritance is that you can create new classes in place of old. So let's try it. We'll create a class, Tally, that behaves much like our Counter class.

a. Create a new class, Tally, that has the following form:

public class Tally
  extends Counter 
{
  public Tally(int start) 
  {
    super(start);
  } // Tally(int)
} // class Tally

b. Change the initialization of alpha so that it reads

    Counter alpha = new Tally(0);

c. What effect to you expect this change to have on the tests or experiments?

d. Check your answer experimentally.

e. How do Tally objects differ from Counter objects? Right now, not at all. How might they differ? We might want to make Tally objects always start at 0, rather than a designated start value. How can we do that? With a slightly different constructor. Replace the constructor of Tally with the following.

  public Tally() 
  {
    super(0);
  } // Tally()

f. What effect do you expect this change to have?

g. Check your answer experimentally.

h. As you might have predicted, Java issues an error message because you are calling the constructor with the wrong number of parameters. Rewrite the initialization to the following and predict the effect.

    Counter alpha = new Tally();

i. Check your answer experimentally.

j. Summarize what you learned in this exercise.

Exercise 3: Decrementable Counters

a. Create a new class, DecrementableCounter, that has the following form:

public class DecrementableCounter 
  extends Counter 
{
  public DecrementableCounter(int start) 
  {
    super(start);
  } // DecrementableCounter(int)
} // class DecrementableCounter

b. Change the initialization of gamma so that it reads

    Counter gamma = new DecrementableCounter(-5);

c. What effect to you expect this change to have on the tests or experiments?

d. Check your answer experimentally.

e. Add a decrement() method to DecrementableCounter This method should subtract one from the count field.

f. What do you expect to happen if we add the following line to our test?

    gamma.reset();
    assertEquals("reset gamma", -5, gamma.value());
    gamma.decrement();
    assertEquals("decremented gamma", -6, gamma.value());

g. Check your answer experimentally.

h. Change the declaration of gamma to

    DecrementableCounter gamma = new DecrementableCounter(-5);

What effect do you expect this change to have?

i. Check your answer experimentally.

j. Change the initialization of gamma so that it reads

    DecrementableCounter gamma = new Counter(-5);

k. What effect to you expect this change to have?

l. Check your answer experimentally.

m. Restore the initialization of gamma to

    DecrementableCounter gamma = new DecrementableCounter(-5);

n. Summarize what you learned in this exercise.

Exercise 4: Naming Counters

a. Create a new class, NamedCounter, that has the following form

public class NamedCounter 
  extends Counter 
{
  String name;
  public NamedCounter(String name, int start) 
  {
    super(start);
    this.name = name;
  } // NamedCounter(String, int)
} // class NamedCounter 

b. Update your test and experiment so that the initialization of alpha reads

    Counter alpha = new NamedCounter("alfa", 0);

c. What effect do you expect this change to have?

d. Check your prediction experimentally.

e. Override the toString method by inserting the following code into NamedCounter.

  @Override
  public String toString()
  {
    return this.name + super.toString();
  } // toString()

f. What effect do you expect this change to have?

g. Check your prediction experimentally.

h. Swap the two lines in the constructor for NamedCounter and determine what errors, if any, you get.

i. Restore the constructor.

j. Summarize what you've learned from this exercise.

Exercise 5: Named Counters, Revisited

a. What effect do you expect if we have NamedCounter extend DecrementableCounter instead of Counter? For example, will we still be able to write the following declaration?

    Counter alpha = new NamedCounter("alfa", 0);

b. Check your answer experimentally.

c. Add a call to System.err.println to each of the constructors so that you can observe when they are called. For example, you might change the NamedCounter constructor to read as follows.

  public NamedCounter(String name, int start) 
  {
    super(start);
    System.err.println("NamedCounter(\"" + name + "\", " + start + ")");
    this.name = name;
  } // NamedCounter(String, int)
} // class NamedCounter 

What do you expect to see as output when your create alpha?

d. Check your answer experimentally.

e. Summarize what you learned from this exercise.

Exercise 6: Double Counters

a. Create a new class, DoubleCounter, that has the following form

public class DoubleCounter 
  extends Counter 
{
} // class DoubleCounter 

b. What do you expect to happen when you compile this class?

c. Check your answer experimentally.

d. Insert a constructor for DoubleCounter of the following form

  public DoubleCounter(int start) 
  {
    super(start);
  } // DoubleCounter(int)

e. Update your experiment so that the initialization of beta reads

  Counter beta = new DoubleCounter(123);

f. What effect do you expect this change to have on your tests or experiments?

g. Check your prediction experimentally.

h. Override the increment method by inserting the following code into DoubleCounter

  @Override
  public void increment() 
  { 
    super.increment();
    super.increment();
  } // increment()

i. What effect do you expect this change to have on your tests and experiments?

j. Check your prediction experimentally.

k. Summarize what you've learned from this exercise.

Exercise 7: Limited Counters

a. Create a subclass of Counter called LimitedCounter that includes

  • an int field named limit;
  • a constructor that takes two parameters: a starting value and an upper limit (that is, a value for the limit field); and
  • a modified increment method that throws an exception when count exceeds limit.

b. In your test, determine the results of changing the initialization of gamma to

  Counter gamma = new LimitedCounter(-5,3);

c. Summarize what you've learned from this exercise.

Exercise 8: Double Counters, Revisited

Note that for this exercise, you probably just want to use the experiment, rather than the test.

. a. Add the following class to your project.

public class DblCtr
  extends Counter;
{
  /**
   * The underlying counter.
   */
  Counter base;

  /**
   * Build a new counter that counts twice as fast as counter.
   */
  public DblCtr(Counter counter)
  {
    super();
    this.base = counter;
  } // DblCtr(Counter)

  /**
   * Increment the counter, twice.
   */
  @Override
  public void increment() 
  { 
    this.base.increment();
    this.base.increment();
  } // increment()
} // class DblCtr

b. Update your experiment so that the initialization of beta reads

  Counter beta = new DblCtr(new Counter());

c. What effect do you expect this change to have on the output?

d. Check your prediction experimentally.

e. Add this toString method to DblCtr

  @Override
  public String toString()
  {
    return this.base.toString();
  } // toString()

f. What effect do you expect this change to have on the output?

g. Check your prediction experimentally.

h. Update your experiment so that the initialization of beta reads

  Counter beta = new DblCtr(new DblCtr(new Counter()));

i. What effect do you expect this change to have on the output?

j. Check your prediction experimentally.

k. Summarize what you learned from this exercise.