SDSU CS 596 Java Programming
Fall Semester, 1998
Clone
To Lecture Notes Index
© 1998, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 10-Oct-98

Contents of Doc 9, Clone


References

The Java Programming Language, 2 ed., Arnold & Gosling, Chapter 3 section 9


Listen Here!S-oct7 3mins Doc 9, Clone Slide # 2

Clone


Java has a mechanism to allow objects to be copied

A class must:

State it implements Cloneable to use the mechanism
Implement the clone method

For the default clone behavior, all the clone method has to do is return super.clone()


clone() is not the same as "new"

clone() copies the values of the fields an object

The clone process does not call either a constructor or any initialization blocks of the cloned object's class

Listen Here!Q-oct1 3mins, S-oct7 3mins, Q-oct8 14mins Doc 9, Clone Slide # 3

Using the Default Clone Process

This example shows the minimum effort needed to support the clone method. We will cover exceptions (the throws CloneNotSupportedException) in later lectures.

 public class Sample implements Cloneable {
   private int size = 0;
   
   public void setSize( int newSize ) { size = newSize; }
   
   public String toString(){
      return "Size: " + size;
   }
   
   public Object clone() throws CloneNotSupportedException {
      return super.clone();
   }
}
public class Test {
   public static void main( String args[] ) 
throws CloneNotSupportedException {
      Sample a = new Sample();
      Sample b = (Sample) a.clone();
      a.setSize( 12 );
      System.out.println( "a " + a );
      System.out.println( "b " + b );
   }
}
Output
a Size: 12
b Size: 0

Listen Here!S-oct7 3secs Doc 9, Clone Slide # 4
Catching the Exception

The exception "CloneNotSupportedException" only occurrs when the clone method is sent to an object that does not support the cloning. Since the class "Sample" below does support cloning, the exception should not occur in the clone method. We can catch the exception in the clone method as shown below. If the exception does occur then there is a problem with the JVM. That is why we then throw an "InternalError".

Note most of the examples of cloning in this document should have been done this way, but were not for lack of space on the hardcopy slides.

public class Sample implements Cloneable {
   private int size = 0;
   
   public void setSize( int newSize ) { size = newSize; }
   
   public String toString(){
      return "Size: " + size;
   }
   
   public Object clone() {
      try {
         return super.clone();
      } catch (CloneNotSupportedException e) { 
         // this shouldn't happen, since we are Cloneable
         throw new InternalError();
      }
   }
}

Listen Here!S-oct7 20secs Doc 9, Clone Slide # 5
clone() is not "new"

This example demonstrates that when you clone an object constructors and initialization block are not called. While not shown here, direct assignments in fields are also not done when cloning objects as they are done when you create an object via "new".

 public class CloneIsNotNew implements Cloneable {
   {
      System.out.println( "In instance initialization block");
   }
   
   public CloneIsNotNew(  ) {
      System.out.println( "In constructor");
   }
   
   public Object clone() throws CloneNotSupportedException {
      return super.clone();
   }
}
public class Test {
   public static void main( String args[] ) throws Exception  {
      CloneIsNotNew a = new CloneIsNotNew();
      System.out.println( "Start clone");
      CloneIsNotNew b = (CloneIsNotNew) a.clone();
      CloneIsNotNew c = (CloneIsNotNew) a.clone();
   }
}
Output
In instance initialization block
In constructor
Start clone

Listen Here!Q-oct1 2mins, S-oct7 3mins Doc 9, Clone Slide # 6

Problems with Default clone()


The default clone method clones the reference but not object or array that is referenced

Using the default clone on an object with a field that is a reference results in two objects with references to the same item. Let Wrapper be a class that has a field, which is a reference to another object. For this example, the other object as a field of type int. Using the default clone process we get two wrapper objects, a and b, which both reference the same object. Any change in the state of this object will be visible through both objects a and b.

   Wrapper a = new Wrapper();

   Wrapper b = (Wrapper) a.clone();


Listen Here!S-oct7 42secs Doc 9, Clone Slide # 7
Using Default clone with Field References

In this example, the class "BadWrapper" has a field that is a reference to a "Sample" object. The class "BadWrapper" uses the default clone process. In class "TestBadWrapper" a "BadWrapper" object is cloned. Now two "BadWrapper" objects reference the same "Sample" object.

 public class Sample implements Cloneable {
   private int size = 0;
   
   public void setSize( int newSize ) { size = newSize; }
   
   public String toString(){ return "Size: " + size; }
   
   public Object clone() throws CloneNotSupportedException {
      return super.clone();
   }
}
public class BadWrapper implements Cloneable {
   private Sample aReference =new Sample();
   
   public void setSize( int newSampleSize ) {
      aReference.setSize( newSampleSize );
   }
   
   public String toString() {
      return "Wrapper " + aReference.toString();
   }
   
   public Object clone() throws CloneNotSupportedException {
      return super.clone();
   }
}

Doc 9, Clone Slide # 8
//Using Default clone with Field References - Continued

Note that the state of "a" is changed, but when we print out "a" and "b", they have the same state. This is because they reference the same "Sample" object.

public class TestBadWrapper
   {
   public static void main( String args[] ) throws Exception 
      {
      BadWrapper a = new BadWrapper();
      BadWrapper b = (BadWrapper) a.clone();
      a.setSize( 12 );
      System.out.println( "a " + a );
      System.out.println( "b " + b );
      }
   }

Output
a Wrapper Size: 12
b Wrapper Size: 12


Listen Here!Q-oct1 6mins, S-oct7 3mins, S-oct7 8mins Doc 9, Clone Slide # 9

Cloning Object References

In this example the field "aReference" is explicitly cloned in the clone method. Now when we clone GoodWrapper objects, each object has its own copies of "Sample" object.
public class GoodWrapper implements Cloneable {
   private Sample aReference =new Sample();
   
   public void setSize( int newSampleSize ) {
      aReference.setSize( newSampleSize );
   }
   
   public String toString() {
      return "Wrapper " + aReference.toString();
   }
   
   public Object clone() throws CloneNotSupportedException {
      GoodWrapper clone = (GoodWrapper) super.clone();
      clone.aReference = (Sample) aReference.clone();
      return clone;
   }
}
public class Test{
   public static void main( String args[] ) throws Exception    {
      GoodWrapper a = new GoodWrapper();
      GoodWrapper b = (GoodWrapper) a.clone();
      a.setSize( 12 );
      System.out.println( "a " + a );
      System.out.println( "b " + b );
   }
}
Output
a Wrapper Size: 12
b Wrapper Size: 0

Listen Here!S-oct7 19mins, Q-oct8 3mins Doc 9, Clone Slide # 10

Cloning Arrays


If we have an array of basic types, then the array must be cloned if we do not want the same array referenced in two or more objects. This example shows one way to clone an array.

public class ArrayWrapper implements Cloneable {
   private int[] aReference = { 0 };
   
   public void setSize( int newSampleSize ) {
      aReference[0] = newSampleSize;
   }
   
   public String toString() {return "Wrapper " + aReference[0]; }
   
   public Object clone() throws CloneNotSupportedException {
      ArrayWrapper clone = (ArrayWrapper) super.clone();
      clone.aReference = (int[]) aReference.clone();
      return clone;
   }
}
public class Test {
   public static void main( String args[] ) throws Exception {
      ArrayWrapper a = new ArrayWrapper();
      ArrayWrapper b = (ArrayWrapper) a.clone();
      a.setSize( 12 );
      System.out.println( "a " + a );
      System.out.println( "b " + b );
   }
}
Output
a Wrapper 12
b Wrapper 0

Doc 9, Clone Slide # 11
How not to Clone an Array of Objects
Just cloning an array is not good enough if the array contains references to objects or other arrays, as this example shows.

public class BadArrayObjectWrapper implements Cloneable {
   private Sample[] aReference = {new Sample()};
   
   public void setSize( int newSampleSize ) {
      aReference[0].setSize( newSampleSize );
   }
   
   public String toString() {
      return "Wrapper " + aReference[0].toString();
   }
   
   public Object clone() throws CloneNotSupportedException {
      BadArrayObjectWrapper clone = (BadArrayObjectWrapper) super.clone();
      clone.aReference = (Sample[]) aReference.clone();
      return clone;
   }
}
public class Test {
   public static void main( String args[] ) throws Exception  {
      BadArrayObjectWrapper a = new BadArrayObjectWrapper();
      BadArrayObjectWrapper b = (BadArrayObjectWrapper) a.clone();
      a.setSize( 12 );
      System.out.println( "a " + a );
      System.out.println( "b " + b );
      }
   }
Output
a Wrapper Size: 12
b Wrapper Size: 12

Doc 9, Clone Slide # 12
How to Clone an Array of Objects
You must clone the array and then clone each element of the array.
public class ArrayObjectWrapper implements Cloneable {
   private Sample[] aReference = {new Sample()};
   
   public void setSize( int newSampleSize ) {
      aReference[0].setSize( newSampleSize );
   }
   
   public String toString() {
      return "Wrapper " + aReference[0].toString();
   }
   
   public Object clone() throws CloneNotSupportedException {
      ArrayObjectWrapper clone = (ArrayObjectWrapper) super.clone();
      clone.aReference = (Sample[]) aReference.clone();
      for (int k = 0; k < aReference.length; k++ )
         clone.aReference[k] = (Sample) aReference[k].clone();
      return clone;
   }
}
public class Test {
   public static void main( String args[] ) throws Exception {
      ArrayObjectWrapper a = new ArrayObjectWrapper();
      ArrayObjectWrapper b = (ArrayObjectWrapper) a.clone();
      a.setSize( 12 );
      System.out.println( "a " + a );
      System.out.println( "b " + b );
   }
}
Output
a Wrapper Size: 12
b Wrapper Size: 0

Listen Here!S-oct7 5mins, Q-oct8 3mins Doc 9, Clone Slide # 13

Non-recursive clone()

There are times when you may not wish to call the clone() method to clone fields of your class. One example is when your field contains circular references (see next slide). Another example is when your field object does not support cloning (see example below). Be warned that copying objects, that do not support clone(), can be very dangerous for two reasons. First, there may be some reason the object did not support clone. Second, you need to copy all the fields in the object.

The example below makes a copy of the field "aReference" by creating a new object, rather than cloning the existing object.

public class Sample {
   private int size = 0;
   
   public void setSize( int newSize ) { size = newSize; }
   public int getSize( ) { return size;}
   
   public String toString(){
      return "Size: " + size;
   }
}
public class Wrapper implements Cloneable {
   private Sample aReference = new Sample();
   
   public void setSize( int newSampleSize ) {
      aReference.setSize( newSampleSize);
   }
   
   public String toString() {return "Wrapper " + aReference; }
   
   public Object clone() throws CloneNotSupportedException {
      Wrapper clone = (Wrapper) super.clone();
      clone.aReference = new Sample();
      clone.aReference.setSize( aReference.getSize() );
      return clone;
   }
}

Listen Here!Q-oct8 2mins Doc 9, Clone Slide # 14

clone() and Circular References


If an object contains a circular reference (x points to y, y points to x), then one has to be careful about cloning that object. The example below shows the problem. When the object x is cloned, the clone method generates an unending cycle of clone() calls.



public class Node implements Cloneable {
   public Node next;
   
   public Object clone() throws CloneNotSupportedException {
      System.out.println( "Start clone");
      Node clone = (Node) super.clone();
      clone.next = (Node) next.clone();
      return clone;
   }
}

public class Test {
   public static void main( String args[] ) throws Exception {
      Node x = new Node();
      Node y = new Node();
      x.next = y;
      y.next = x;
      // The clone causes infinite recursion
      Node trouble =(Node) x.clone();
   }
}


Copyright © 1998 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
All rights reserved.

visitors since 27-Sep-98