Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

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

How to avoid traps and correctly override methods from java.lang.Object

Avoid incorrect implementations and bugs by following these guidelines

  • Print
  • Feedback

Page 2 of 5

The second theme is that methods have contracts -- defined behavior -- and when implementing or overriding a method, the contract should be fulfilled. The equals method of Object provides an example of a contract: the contract states that if the parameter to equals is null, then equals must return false. When overriding equals, you are responsible for ensuring that all the specifics of the contract are still met.

Implementing clone

The clone method allows clients to obtain a copy of a given object without knowing the precise class of the original object. The clone method in Object is a magic function that generates a shallow copy of the entire object being cloned.

To enable shallow cloning of your class, you implement the Cloneable interface. (For a full discussion of shallow copying versus deep copying, see the sidebar below.) Since Cloneable is a tagging interface with no methods, it's simple to implement:

    public class BaseClass implements Cloneable
    {
        // Rest of the class.

// Notice that you don't even have to write the clone method! }


clone is a protected method. If you want objects from other packages to be able to call it, you must make clone public. You do this by redeclaring clone and then calling the superclass's clone method:

    public class BaseClass implements Cloneable
    {
        // Rest of the class.

public Object clone () throws CloneNotSupportedException { return super.clone(); } }


Finally, if you want some of the member data in the class to be copied deeply, you must copy these members yourself:

    public class BaseClass implements Cloneable
    {
        // SomeOtherClass is just an example.  It might look like
        // this:
        //
        //     class SomeOtherClass implements Cloneable
        //     {
        //         public Object clone () 
                        throws CloneNotSupportedException
        //         {
        //             return super.clone();
        //         }
        //     }
        //
        private SomeOtherClass data;

// Rest of the class.
public Object clone () throws CloneNotSupportedException { BaseClass newObject = (BaseClass)super.clone();
// At this point, newObject shares the SomeOtherClass // object referred to by this.data with the object // running clone. If you want newObject to have its own // copy of data, you must clone this data yourself.

if (this.data != null) newObject.data = (SomeOtherClass)this.data.clone();
return newObject; } }


That's it. So, what mistakes should you look out for?

  1. Don't fail to implement the Cloneable interface if you want your class to be cloneable. The clone method from Object checks that the Cloneable interface has been implemented. If the Cloneable interface hasn't been implemented, a CloneNotSupportedException is thrown when clone is called.

  2. Don't implement clone by using a constructor. The javadoc for the clone method states that it:

    Creates a new object of the same class as this object. It then initializes each of the new object's fields by assigning it the same value as the corresponding field in this object. No constructor is called.


    Notice that "no constructor is called." Avoid implementing clone as follows:

        public class BaseClass implements Cloneable
        {
            public BaseClass (/* parameters */)
            {
                // Code goes here...
            }
    
    // Rest of the class.
    public Object clone () throws CloneNotSupportedException { return new BaseClass (/* parameters */); } }


    There are two reasons to avoid such an approach: First, the contract for clone states that no constructor is called. Second, and more importantly, child classes now return the wrong type from clone. In the example below, the object returned by clone is a BaseClass, not a ChildClass!

        public class ChildClass extends BaseClass
        {
            // Use clone from BaseClass
        }
    


    Further, the child class cannot override clone to make a deep copy of the member variables in the ChildClass. The following code demonstrates this problem:

        public class ChildClass extends BaseClass
        {
            private SomeOtherClass data; 
    
    // Rest of the class.
    public Object clone () throws CloneNotSupportedException { // The cast in the line below throws an exception! // ChildClass newObject = (ChildClass)super.clone();
    // You _never_ get here because the line above throws // an exception. if (this.data != null) newObject.data = (SomeOtherClass)this.data.clone();
    return newObject; } }


    The first line in clone throws an exception because the clone method in BaseClass returns a BaseClass object not a ChildClass object.

    Summary: Don't implement clone by using a copy constructor.

  3. Avoid using constructors to copy subobjects when possible. Another mistake is to use constructors to copy subobjects when implementing clone. Consider the following example class, which uses Dimension as the subobject:

        import java.awt.Dimension;
    
    public class Example implements Cloneable { private Dimension dim;
    public void setDimension (Dimension dim) { this.dim = dim; }
    public Object clone () throws CloneNotSupportedException { Example newObject = (Example)super.clone();
    // Notice the use of a constructor below instead of // a clone method call. If you have a sub-class of // Dimension, any data in the sub-class (e.g. a third // dimension value like z) will be lost. // if (this.dim != null) newObject.dim = new Dimension (dim);
    return newObject; } }


    If a child class of Dimension is passed to setDimension, the object returned by clone will be different from the original object. The preferred way to write this clone method would be:

        import java.awt.Dimension;
    
    public class Example implements Cloneable { private Dimension dim;

    public void setDimension (Dimension dim) { this.dim = dim; }
    public Object clone () throws CloneNotSupportedException { Example newObject = (Example)super.clone();
    // Call 'this.dim.clone()' instead of // 'new Dimension(dim)' // if (this.dim != null) newObject.dim = (Dimension)this.dim.clone();
    return newObject; } }


    Now, if a child class of Dimension is passed to setDimension, it is copied properly when clone is called.

    Unfortunately, while the preferred code above compiles under the Java 2 platform (formerly known as JDK 1.2), it won't compile under JDK 1.1.7. Dimension doesn't implement Cloneable in JDK1.1 and the clone method for Dimension is protected so Example can't call it anyway. This means that under JDK 1.1 you must write Example's clone method using a copy constructor for the Dimension member variable even though you don't want to. If a child of Dimension is passed to setDimension, you'll have a problem if you try to clone an Example object.

    Testing explicitly for Dimension in the clone method is one workaround:

        import java.awt.Dimension;
    
    public class Example implements Cloneable { private Dimension dim;
    public void setDimension (Dimension dim) { this.dim = dim; }
    public Object clone () throws CloneNotSupportedException { Example newObject = (Example)super.clone();
    if (this.dim != null) { // Test explicitly for Dimension here. Don't test // using the instanceof operator -- it doesn't do // what you want it to. // if (this.dim.getClass() != Dimension.class) throw new CloneNotSupportedException("Wrong sub-class for 'dim'");
    newObject.dim = new Dimension (dim); } return newObject; } }


    This is better than returning a clone object with the wrong data for dim, but it still isn't a good solution.

  • Print
  • Feedback

Resources
  • Both the javadoc and the source code for the methods in java.lang.Object can be found at Sun's Java Web site http://java.sun.com
  • The Eiffel programming language takes the notion of design-by-contract very seriously. This paper provides a good discussion of this notion http://www.eiffel.com/doc/manuals/technology/contract/index.html