Canonical Form of a Java Class

Joseph Bergin
Pace University
berginf@pace.edu

Most Java classes need at least the following parts. This is beyond any functionality that it encapsulates. The names of classes are capitalized in Java as a style convention.

public class Name extends Object implements Cloneable, java.io.Serializable
{ 	public Name()
	{	... DO NOT call non final methods from here.
	}

	public String toString()
	{	return ... 
	}

	public boolean equals(Object o)
	{	... Defines an Equivalence relation
			Reflexive: a.equals(a) must return true.
			Symmetric: If a.equals(o) then o.equals(a) as well. 
			Transitive: If a.equals(b) and b.equals(c), then a.equals(c).
		... Note that Symmetric is usually the difficult one to be sure of. 
	}

	public int hashCode()
	{	... Consistent with equals: two "equals" objects should have same hashCode result.
	}
}

Almost all classes should have a constructor with no arguments. Sometimes this is not logical, so it is not a firm requirement. If a class does not have such a constructor, then the constructors of all subclasses will need to explicitly call one of the constructors of your class with the super notation.

If you call a non-final method of a class from a constructor of that class the wrong thing is likely to happen, since the object is still under construction but calling polymorphic methods requires that the object be complete.

The toString method is used by the Java input/output system and by some othe parts of the Java system. It is also very useful when finding errors in the program, since if it produces good information about the object and its fields, the programmer can see what is going on in a program just by writing out some of the objects of interest.

The usual body of equals is something like

public boolean equals(Object o)
{	if(o == null) return false;
	if(getClass() != o.getClass()) return false;
	return ... depending on fields of o and this. 
}

The default (Object) implementation of equals uses == which is reference equality. Usually this is the wrong comparison to make, since it doesn't take the meaning of the object into consideration. The hashCode method is needed so that the objects can be put into hash tables (Hashtable and HashMap) as the keys. If hashCode is not consistent with equals, the hash table will fail to work properly. If the equals test is based on some set of fields of the class, then hashCode should be based on those same fields. (Thanks to Owen Astrachan of Duke University for pointing out an error in an earlier version.)

It is extremely important that your equals method take a parameter of type Object. If you overload this method, giving it a more specific parameter, as seems intuitively correct, many things in the system will fail to work correctly. This is because equals is called by system code in quite a few situations and your more specific one will never be called this way since dynamic polymorphism isn't used on parameters.

Most classes should implement java.io.Serializable, which has no requirements. It permits saving the object in a file or transmitting it across the net.

Most classes should implement Cloneable and have a method to return a faithful copy:

	public Object clone()
	{	...
	}

By default this method is protected and if called will throw the CloneNotSupportedException

Note that you don't need to use the constructor to initialize most fields. You can do this in the declarations--even if you need to call a function to do so.

Also. Your fields should all be private. If subclasses need access to some of the fields, you can provide protected accessors.

NOTE for Java 2 users. If you are building a class in Java 2 (Java version 1.2 or later) you should also consider if it is appropriate for your class to implement the Comparable interface as well. If it does not, then objects in your class will not be able to serve as keys in TreeMaps. If you do decide it is appropriate you need to also implement the method

public int compareTo(Object other)
{	...
}

This method returns a negative if this can be considered to be "less" than other, a positive if this is "greater" than other and zero if they are the same. Note that equals should be consistent with compareTo so that if equals is true then compareTo gives zero.


As of Java 5 most classes (at least those with objects that want to be sortable), should implement the Comparable interface (from java.lang).

	class Foo implements Comparable<Foo>{
		int compareTo(Foo value){
			...
		}
	} 
 

This permits objects of this class to be compared to each other. If a should be thought of as "less" than b then a.compareTo(b) should return a negative value. Generally speaking if a.equals(b), then a.compareTo(b) should return 0. Also a.compareTo(b) should have the opposite sign as b.compareTo(a). In fact compareTo should form an equivalence relation on the values (symmetric, transitive, and reflexive) as well as a total order. Then the methods of java.util.Collections can sort the elements automatically when they are stored in arrays, etc. It is also easy to put them into sorted collections, such as sorted list, though there are other ways to achieve that.


This page has been translated into Russian and into Ukrainian.
The translations of this article are provided by azoft thanks to Alexei Kondratiev.

Last Updated: January 4, 2011