Algorithms and OOD (CSC 207 2014F) : Labs
Primary: [Front Door] [Schedule] - [Academic Honesty] [Disabilities] [Email] - [Learning Outcomes] [FAQ] [Teaching & Learning] [Grading] [Rubric] - [Calendar]
Current: [Assignment] [EBoard] [Lab] [Outline] [Reading]
Sections: [Assignments] [EBoards] [Examples] [Handouts] [Labs] [Outlines] [Readings]
Reference: [Student-Curated Resources] [Java 8 API] [Java 8 Tutorials] [Code Conventions]
Related Courses: [CSC 152 2006S (Rebelsky)] [CSC 207 2014S (Rebelsky)] [CSC 207 2014F (Walker)] [CSC 207 2011S (Weinman)]
Misc: [Submit Questions] - [SamR] [Glimmer Labs] [CS@Grinnell] [Grinnell] - [Issue Tracker (Course)] [Issue Tracker (Textbook)]
Summary: In today's laboratory, you will explore inheritance in Java by building and extending some simple classes.
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.)
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
int fields,
count and start.
count and
start to that value.
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
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.
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.
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.
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.
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.
a. Create a subclass of Counter called
LimitedCounter that includes
int field named limit;
limit field);
and
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.
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.