Recipe 9.4 The Clone Method


Problem

You want to clone yourself. Or at least your objects.

Solution

Override Object.clone( ) .

Discussion

To clone something is to make a duplicate of it. The clone( ) method in Java makes an exact duplicate of an object. Why do we need cloning? Think about what happens when you call a method passing it an argument you have created. Java's method-calling semantics are call-by-reference a reference to your object is passed which allows the called method to modify the state of your carefully constructed object! Cloning the input object before calling the method allows you to pass a copy of the object, keeping your original safe.

How can you clone? Cloning is not "enabled" by default in classes that you write:

Object o = new Object( ); Object o2 = o.clone( );

If you try calling clone( ) without any special preparation, as in this excerpt from Clone0.java, you will see a message like this (from the Jikes compiler; the javac message may not be as informative):

Clone0.java:4:29:4:37: Error: Method "java.lang.Object clone( );" in class "java/lang/ Object" has protected or default access. Therefore, it is not accessible in class  "Clone0" which is in a different package.

You must take two steps to make your class cloneable:

  1. Override Object's clone( ) method.

  2. Implement the empty Cloneable interface.

Using cloning

The class java.lang.Object declares its clone method protected and native . Protected methods can be called by a subclass or those in the same package (i.e., java.lang), but not by unrelated classes. That is, you can call Object.clone( ) the native method that does the magic of duplicating the object only from within the object being cloned. Here is a simple example of a class with a clone method and a tiny program that uses it:

public class Clone1 implements Cloneable {     /** Clone this object. Just call super.clone( ) to do the work */     public Object clone( )  {         try {             return super.clone( );         } catch (CloneNotSupportedException ex) {             throw new InternalError(ex.toString( ));         }     }          int x;     transient int y;    // will be cloned, but not serialized     public static void main(String[] args) {          Clone1 c = new Clone1( );         c.x = 100;         c.y = 200;         try {             Object d = c.clone( );             System.out.println("c=" + c);             System.out.println("d=" + d);         } catch (Exception ex) {             System.out.println("Now that's a surprise!!");             System.out.println(ex);         }     }     /** Display the current object as a string */     public String toString( ) {         return "Clone1[" + x + "," + y + "]";     } }

The clone( ) method in Object throws CloneNotSupportedException . This handles the case of inadvertently calling clone( ) on a class that isn't supposed to be cloned. Since most of the time you don't need to do anything with this exception, a clone method could declare this exception in its throws clause and let the calling code deal with it. But CloneNotSupportedException is a checked exception, and it's generally unlikely, so it's often simpler to have your clone( ) method catch it and rethrow it as an unchecked exception, as this code does.

Calling Object's clone( ) creates a stateful, shallow copy down inside the JVM. That is, it creates a new object, and copies all the fields from the old object into the new. It then returns the new reference as an Object; you need to cast it to the appropriate object type. So if that's all there is, why do you even have to write this method? The reason is to give you a chance to do any preservation of state that is required in cloning your objects. For example, if your class has any references to other objects (and most real-world classes do), you may well want to clone them as well! The default clone method simply copies all the object's state so that you now have two references to each object. Or you might have to close and reopen files to avoid having two threads (see Chapter 24) reading from or writing into the same file. In effect, what you have to do here depends on what the rest of your class does.

Now suppose that you clone a class containing an array of objects. You now have two references to objects in the array, but further additions to the array will be made only in one array or the other. Imagine a Vector, Stack, or other collection class being used in your class, and your object gets cloned!

The bottom line is that most object references need to be cloned.

Even if you don't need clone( ), your subclasses may! If you didn't provide clone( ) in a class subclassed from Object, your subclasses will probably get the Object version, which causes problems if there are collections or other mutable objects referred to. As a general rule, you should provide clone( ) even if only your own subclasses would need it.

Difficulty in the standard API

The java.util.Observable class (designed to implement the Model-View-Controller pattern with AWT or Swing applications) contains a private Vector but no clone method to deep-clone it. Thus, Observable objects cannot safely be cloned, ever!



Java Cookbook
Java Cookbook, Second Edition
ISBN: 0596007019
EAN: 2147483647
Year: 2003
Pages: 409
Authors: Ian F Darwin

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net