Item 11: Override clone judiously

Before talking on the clone method in java, I must list some very important facts.

  1. Object class has a clone method, but it is protected. So, it is not available to our objects.
     
  2. One can always override the clone method, but you will get CloneNotSupportedException unless you make the class implement Cloneable interface and implement the clone method.
     
  3. Java’s default clone method supports only a shallow copy of the object.
    a) super.clone returns an object of class Object
    b) Shallow copy of an object mean the copy of an object without moving to deeper levels. i.e. in case a class has an array, then the elements in the new object’s array will have the same references as the elements in array of the original object.
     
  4. For a deep copy of objects, one must prefer using a copy constructor over the copy method.
     
  5. The clone method is supposed to follow a not very well defined contract.
    x.clone() != x
    x.clone().getClass() == x.getClass()

    x.clone().equals(x) 
     

Clone and Equals

The third part of the 5th statement makes it quite clear that, one should keep the clone and equals method consistent, in order to achieve the contract of the clone.

If you override the clone method in a non-final class, you should return an object obtained by invoking super.clone

In case you want to make a shallow copy of some object falling very low in the class hierarchy, just make sure that super classes implement ‘Cloneable’ and they invoke the super.clone method.
Never make the client do anything the library can do for the client
The super.clone() method returns an object of class Object. It is the duty of the developer to cast the object to the proper class.
Lets discuss some of the don’t and shortcomings of clone method.
Problem with auto cloning chain

Now, that we are on the same page as far as clone method is concerned, let us try and understand clone using a simple example. Consider the class mention below.

public class Stack {
	private Object[] elements;
	private int size = 0;
	private static final int DEFAULT_INITIAL_CAPACITY = 16;

	public Stack() {
		this.elements = new Object[DEFAULT_INITIAL_CAPACITY];
	}

	public void push(Object e) {
		ensureCapacity();
		elements[size++] = e;
	}

	public Object pop() {
		if (size == 0)
			throw new EmptyStackException();
		Object result = elements[--size];
		elements[size] = null; // Eliminate obsolete reference
		return result;
	}

	// Ensure space for at least one more element.
	private void ensureCapacity() {
		if (elements.length == size)
			elements = Arrays.copyOf(elements, 2 * size + 1);
	}
}

Shallow copy dread

In this particular example, consider making a shallow copy of the Stack object. The shallow copy will have a new elements array, but the elements in the new array will still refer to where the elements of the original elements array referred.

One must ensure that the original object doesn’t suffer due to the copy method

As we saw, same references will be used with the new object as well. Modification to these references using the new object will also affect the original object.

The clone architecture is incompatible with normal use of final fields referring to mutable objects

In case you use fields which are final, you must think of implementing the copy method yourself.

Clone methods in thread safe classes must be synchronized explicitly

In case you are using a class that is thread safe, you may need to synchronize your clone method.

The solution to this problem is: Deep Copy

Deep Copy

// Iteratively copy the linked list headed by this Entry
Entry deepCopy() {
	Entry result = new Entry(key, value, next);
	for (Entry p = result; p.next != null; p = p.next)
		p.next = new Entry(p.next.key, p.next.value,
				p.next.next);
	return result;
}

As you can see each entry in the result now references a new object of the Entry object. Resulting in a deep copy.

Summary

  1. While implementing Cloneable, one must override clone with a public method whose return type is the class itself.
  2. This method must first call super.clone and then fix anything that is needed to be fixed. i.e. it should be taken care of, that the internal deep structure must not be compromised. Mostly such cases have to be handled for the mutable objects.
  3. Serial version Id must not be cloned. This should be taken care of explicitly.
  4. Prefer copy constructor over clone.
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: