unimportant logo identifying Barry Cornelius

University of Oxford

Computing Services

Design of classes

  Author: Barry Cornelius Date: January 2001


1. minimal public interface

2. Providing equals

2.1. The need to provide equals with an Object parameter

2.2. Using getClass in the code of equals

Since the equals method appears in a class called Date, you would think that the target of the equals method must be an object of class Date, and so this kind of call of getClass will always return the class Date. However, suppose we derive a class called NamedDate from Date:

public class NamedDate extends Date
   private String iName:
Suppose that NamedDate does not override equals. If we write:
then this will be a call of Date's equals method and both calls of getClass will return the class NamedDate. So, even though this code appears in the class declaration for Date, in some circumstances the first call of getClass will return a value that is a subclass of the class Date.

2.3. Using getClass in preference to instanceof

3. Providing hashCode

3.1. The need to provide hashCode

3.2. The contract that hashCode should satisfy

3.3. Providing a hashCode method that returns the value 0

3.4. Providing other code for the hashCode function

For example, suppose we want to set up a HashSet containing the dates when various composers died:

Bach 1750-08-28
Beethoven 1827-03-26
Cage 1992-08-12
Chopin 1849-10-17
Copland 1990-12-02
Elgar 1934-02-23
Handel 1759-04-14
Mendelssohn 1847-11-04
Purcell 1695-11-21
Sibelius 1957-09-20
Stanford 1924-03-29
Tallis 1585-11-23
Tchaikovsky 1893-11-06
Vaughan-Williams 1958-08-26
Walton 1983-03-08

Suppose we add each of these dates to a HashSet, e.g. for Bach:

final Date tDeathOfBach = new Date(1750, 8, 28);

The add method could use Date's hashCode function to store the values in 12 buckets:

  1. 1934-02-23
  2. 1924-03-29, 1827-03-26, 1983-03-08
  3. 1759-04-14
  4. 1750-08-28, 1992-08-12, 1958-08-26
  5. 1957-09-20
  6. 1849-10-17
  7. 1847-11-04, 1695-11-21, 1893-11-06, 1585-11-23
  8. 1990-12-02

Then, when later we ask the collection class whether it has the value 1893-11-06 (the date when Tchaikovsky died), the contains method can call hashCode on this value and, because this produces the value 11, the contains method need only check the values in the 11th bucket. The code of the contains method uses equals on each of these values in turn returning the value true if and only if it finds the value (in this case, the value 1893-11-06).

3.5. The reason for using a zero-returning hashCode

3.6. It is not a problem for immutable classes

3.7. Another rule

3.8. What is the effect on performance?

4. Providing compareTo

4.1. The need to provide compareTo

4.2. Using the Comparator interface

If a class such as Date fails to implement the Comparable interface, or its implementation of compareTo provides inappropriate code, a client class can still store Date objects in a TreeSet or a TreeMap provided it creates the TreeSet/TreeMap using a constructor that is passed an object that implements the Comparator interface ([7]). This interface requires the object to provide the method:

public int compare(Object pObject1, Object pObject2);
This method returns a negative integer, zero, or a positive integer depending on whether the value of pObject1 is less than, equal to, or greater than that of pObject2.

If, bizarrely, we chose to compare dates only on the year field, we could provide:

public class MyDateComparator implements java.util.Comparator
   public int compare(final Object pObject1, final Object pObject2)
      return ((Date)pObject1).getYear() - ((Date)pObject2).getYear();
and then use:
MyDateComparator tMyDateComparator = new MyDateComparator();
final Set tOccurrences = new TreeSet(tMyDateComparator):

5. Providing a clone

5.1. The need to provide a cloning operation

5.2. Providing a constructor for cloning

5.3. Providing a method called clone

Object's clone method only produces a shallow copy. So, if a class has one or more fields that are of a reference type, we may want to provide a deep copy by cloning these fields. For example, for the Person class, we may want to provide:

public class Person implements Cloneable
   public Object clone()
         final Person tPerson = (Person)super.clone();
         if (iDateOfBirth!=null)
            tPerson.iDateOfBirth = (Date)iDateOfBirth.clone();
         return tPerson;
      catch(final CloneNotSupportedException pCloneNotSupportedException)
         throw new InternalError();

5.4. Other information about cloning

For more information about how to clone objects, look at the canonical object article by Bill Venners ([11]).

6. References

  1. Barry Cornelius, 'Teaching a Course on Understanding Java', http://www.ics.ltsn.ac.uk/pub/Jicc4/
  2. James Gosling, Bill Joy, Guy Steele, Gilad Bracha, 'The Java Language Specification, Second Edition', Addison-Wesley, 2000, 0-201-31008-2
  3. Peter Haggar, 'Practical Java', Addison-Wesley, 2000, 0-201-61646-7
  4. Arthur Riel, 'Object-Oriented Design Heuristics', Addison-Wesley, 1996, 0-201-63385-X
  5. Mark Roulo, 'How to avoid traps and correctly override methods from java.lang.Object', http://www.javaworld.com/javaworld/jw-01-1999/jw-01-object.html
  6. Sun Microsystems, 'java.lang.Comparable', http://java.sun.com/j2se/1.3/docs/api/java/lang/Comparable.html
  7. Sun Microsystems, 'java.util.Comparator', http://java.sun.com/j2se/1.3/docs/api/java/util/Comparator.html
  8. Sun Microsystems, 'java.lang.Object.equals', http://java.sun.com/j2se/1.3/docs/api/java/lang/Object.html#equals(java.lang.Object)
  9. Sun Microsystems, 'java.lang.Object.hashCode', http://java.sun.com/j2se/1.3/docs/api/java/lang/Object.html#hashCode()
  10. Sun Microsystems, 'java.util.Hashtable', http://java.sun.com/j2se/1.3/docs/api/java/util/Hashtable.html
  11. Bill Venners, 'The canonical object idiom', http://www.javaworld.com/javaworld/jw-10-1998/jw-10-techniques.html

Date: January 2001 Author: Barry Cornelius.
This page is copyrighted