Using the Java Class Checklist
Thomas J. Kennedy
Throughout this semester we have discussed the cross-language class checklist throughout each module. We started with C++ as the initial language, then discussed Java, briefly discussed Python, and introduced Rust.
This discussion will focus on the Java Class Checklist, specifically in how to use the Java Class Checklist as a guide to write a complete Java Class.
1 The Initial Class
Suppose you are working on a team, and one group member (the team smart aleck) has written a quick-and-dirty Cookbook
class.
/**
* A collection of Recipes. This class handles all logic
* for a cookbook--e.g.,
* - Adding recipes
* - Printing recipes
*/
public class Cookbook {
private static final int MAX_RECIPES = 100;
private Recipe[] recipes;
/**
* Create a Cookbook that can contain at most MAX_RECIPES
* recipes
*/
public Cookbook()
{
//...
}
/**
* Create a Cookbook that can contain at most _r_
* recipes
*/
public Cookbook(int r)
{
//...
}
/**
* Add a Recipe
*
* @param toAdd new Recipe
*/
public void addRecipe(Recipe toAdd)
{
//...
}
}
You are responsible for the Cookbook
class code review. What changes does Cookbook
need to be considered complete?
Before reading the remainder of this document, take a few moments to think about what is missing.
2 Initial Observations
We know based on the Cross-Language Class Checklist that Cookbook
is incomplete.
3 Starting with toString
The toString
method is the most natural function (i.e., it has a clear purpose). There will always be a need to output a custom object. In C++ this is handled by operator<<
paired with display
. In Java output (more aptly generating a human readable string) is handled by toString
/**
* List each Recipe, seperating them with a blank line followed by "---"
* and a second blank line.
*/
@Override
public String toString()
{
//...
}
Note the @Override
decorator. The toString
method is provided by the Java Object
base class (which outputs the memory address of the object). I need to mark Cookbook.toString
as overridden.
Normally I would forgo Javadoc documentation for an override function. However, the documentation must always establish the rules of a function. In this case, I need to know what toString
will output.
4 Tackling equals and hashCode
The Java equals
function is equivalent to the C++ operator==
. The two serve the same purpose (i.e., to check two objects for equivalence).
/**
* Compare two recipes based on the recipes they contain, ignoring the
* the order of the recipes.
*
* @param rhs object against which to compare
*
* @return true if both this and rhs are Cookbooks with the same recipes
*/
@Override
public boolean equals(Object rhs)
{
//...
}
In C++, operator<
is usually paired with operator==
based on requirements of the C++ STL. Java is similar. The equals
and hashCode
functions are all-but-required to be paired together. Both functions must follow the same rules. In this case, both equals
and hashCode
are based purely on the Recipe
objects contained in a Cookbook
.
/**
* Compute the hashcode by adding all recipe hashcodes together.
*
* @return integer hashcode
*/
@Override
public int hashCode()
{
//...
}
5 Interface Completeness
The initial Cookbook
class has an addRecipe
function. If the class allows Recipe
s to be added, removal of Recipe
s should probably be possible too.
/**
* Remove a Recipe
*
* @param toRemove index of Recipe to remove
*/
public void removeRecipe(int toRemove)
{
//...
}
6 Big-3 and clone
In Java the C++ Big-3 become a single Java clone
function. I need to be able to create a deep copy. Adding clone requires two changes. First, I need to modify the initial class line:
public class Cookbook
implements Cloneable
The implements Cloneable
indicates that this class override the clone
method:
/**
* Create a deep copy.
*/
@Override
public Cookbook clone()
{
//...
}
This clone
method takes the place of a Copy Constructor.
7 Iterators
A Cookbook
has multiple Recipe
s. What design pattern allows us to iterator over a collection? The Iterator Pattern.
Similar to clone
, we must implement an interface. This time the interface is templated:
public class Cookbook
implements Cloneable, Iterable<Recipe>
The Iterable
interface requires that an iterator
function be provided.
/**
* This depends on implementation decisions (e.g., selected data structure).
*/
@Override
public Iterator<Recipe> iterator()
{
//...
}
8 Putting the Pieces Together
We have discussed the flaws in the initial Cookbook class. Putting the code snippets together is a good opportunity to practice a little Java. I will leave writing the full, complete, and corrected Cookbook
class as an exercise to the reader.