The Artima Developer Community
Sponsored Link

Canonical Object Idiom
Defining a Baseline Set of Functionality for Objects
by Bill Venners
First Published in JavaWorld, September 1998

<<  Page 2 of 4  >>

Advertisement

Recipe: How to concoct a canonical object
You can turn instances of any class into canonical objects by taking the following steps with the class:

  1. Implement Cloneable (unless a superclass already implements it or the object is immutable).

  2. If the class includes instance variables that may at some point in the lifetime of its instances hold references to mutable objects, override clone().

  3. Override equals() and hashCode().

  4. Implement Serializable (unless a superclass already implements it).

Example code
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.

<<  Page 2 of 4  >>


Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us