Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
I call this object idiom "canonical" because it represents the simplest form an object should take. The idea behind this idiom is to suggest a baseline functionality that you give by default to any object you design. You may adopt this idiom in your own programming practice, or, if you are developing a set of style guidelines for a team or organization, you may want to make the idiom part of those guidelines. The intent of the idiom is not to force programmers to endow every object with this baseline functionality, but simply to define a default functionality for every object. In other words, programmers would be encouraged to make a new object canonical unless they have specific reasons to depart from the idiom in a given, specific case.
A fundamental tenet of object-oriented programming is that you can treat an instance of a subclass as if it were an instance
of a superclass. Because all classes in Java are subclasses of java.lang.Object, it follows that another basic tenet of Java programming is that you can treat any object as, well, an Object.
As a Java programmer, you can treat an object as an Object in many ways. When you invoke any of the methods declared in class java.lang.Object on an object, for example, you are treating that object as an Object. These methods, some of which are clone(), equals(), toString(), wait(), notify(), finalize(), getClass(), and hashCode(), provide basic services commonly exercised on Java objects of all kinds. In addition, you may find yourself wanting to serialize
just about any kind of object. All of these activities are ways you may treat objects of many diverse classes simply and uniformly
as Objects.
Taken together, all the activities that are commonly performed on Java objects constitute a set of services that, in most cases, should be built into every class you design. The question raised by this article and answered by the idiom is: What do you need to do, at a minimum, to make each object you define support the services commonly expected of all objects?
This idiom defines a baseline set of requirements for object definitions and names objects that implement the baseline "canonical objects."
You can turn instances of any class into canonical objects by taking the following steps with the class:
Cloneable (unless a superclass already implements it or the object is immutable).clone().equals().Serializable (unless a superclass already implements it).
Here's a Java class that illustrates the canonical object idiom:
// In file canonical/ex1/Worker.java import java.io.Serializable; import java.util.Vector;
public class Worker implements Cloneable, Serializable {
private String name; private Vector doList;
public Worker(String name, Vector doList) { if (name == null || doList == null) { throw new IllegalArgumentException(); } this.name = name; this.doList = doList; }
public Worker(String name) { this(name, new Vector()); }
public void setName(String name) { if (name == null) { throw new IllegalArgumentException(); } this.name = name; }
public void addtoList(Object job) { doList.addElement(job); }
public Object clone() {
// Do the basic clone Worker theClone = null; try { theClone = (Worker) super.clone(); } catch (CloneNotSupportedException e) { // Should never happen throw new InternalError(e.toString()); }
// Clone mutable members theClone.doList = (Vector) doList.clone(); return theClone; }
public boolean equals(Object o) {
if (o == null) { return false; }
Worker w; try { w = (Worker) o; } catch (ClassCastException e) { return false; }
if (name.equals(w.name) && doList.equals(w.doList)) { return true; } return false; }
//... }
In the code listing above, instances of class Worker are canonical objects because the Worker objects are ready for (1) cloning, (2) serialization, and (3) semantic comparison with equals. To make Worker objects ready for cloning, class Worker implements Cloneable. Implementing Cloneable is necessary in this case because Worker objects are mutable and Cloneable isn't implemented by any superclass. Likewise, to make Worker objects ready for serialization, class Worker implements Serializable. Because no superclass of Worker implements Serializable, class Worker itself must implement it. Lastly, class Worker, like any other class with canonical instances are canonical objects, overrides equals() with a method that does an appropriate semantic comparison of the two objects.