In designing a class, it is sometimes useful to have an operation that makes a copy, or clone, of the object. Ideally, the result should be two separated objects so that an updated to one will not affect the other. Some languages such as C++ provide this capability with a copy constructor. The idea is illustrated in the String class, which has a constructor that takes a string argument and create a new string with the same sequence of characters.
In Java, the standard approach has a class using a cloning mechanism that relies on the Object clone() method. The method performs a field-for-field copy of the object. The following is a declaration of clone() in the Object class. The keyword native means that the implementation of the method is in another programming language, usually C.
Because all classes are derived from Object, the access modifier protected makes Object clone() available to any method in a class declaration but prevent it from being called by any instance of a class. For a class to make cloning available for its objects, the class must implement a public version of clone() and implement the Cloneable interface :
Cloneable is an interesting interface. It has no methods and only indicates to the Object clone() method that it is legal for that method to make a field-for-field copy of instances of the class. A method throws the CloneNotSupportedException if it calls clone() for an object whose class type doesn't implement the Cloneable interface.
Cloning Time24 Objects :
We illustrate cloning by adding the capability to the Time24 class. The declaration of clone() in the class illustrates how we use the Object method clone() to copy the instance variables hour and minute and thus create a new object. The class header adds Cloneable to the implementation list. The Time24 clone() method calls the Object method clone() using the syntax super.clone() :
Let us use Time24 clone() to create a new and separated object. The Time24 object tA is 8:30 and the clone is object tB :
- Time24 tA = new Time24(8,24), tB;
- tB = (Time24)tA.clone();
The Time24 class has only primitive data fields. In a clone, all of the data has been duplicated. This is termed a deep copy. Updates to the original object or to its clone do not affect the other object. For instance, look what happens whan addTime() advances tA by 15 and tB by 45 minutes :
- tA.addTime(15); // tA is now 8:45 (8:30+0:15)
- tB.addTime(45); // tB is now 9:15 (8:30+0:45)
Cloning Reference Variables :
When a class has a reference variable, cloning still produces a separated object. However, the original object and the copy are not independent in the sense that an update to one may also affect the other. An example illustrates this idea. The class CloneRef has a primite integer variable n and a Time24 reference variable t as data fields. The cloning algorithm for the Time24 class applies to the CloneRef class :
To illustrate the relationship between the object and its clone, we add to the CloneRef class methods updateInt(n) and updateTime(m). The former assigns the argument n as the new primitive value; the latter uses addTime() to advance the time of the reference object t by m minutes :
The effect of the update methods on an object and its copy reveal an import property of cloning. Assume crA is CloneRef object with integer value 20 and time 10:15. Using clone(), we create crB as a copy of crA :
- CloneRef crA = new CloneRef(20, 10, 15);
- CloneRef crB = (CloneRef)crA.clone();
Using method updateInt(55) with the original object crA changes the value of its primitive variable. Using updateTime(30) with either crA OR crB advances time for the instance variable t. The variable t in each CloneRef object points to the same Time24 object, which is now 10:45 :
Cloning an object with reference variables creates two separated objects. However, an update of the reference object affects both the original object and the copy. This is termed a shallow copy.
Cloning an ArrayList :
The cloning mechanism that we used for the CloneRef class applies to collection classes. Additional overhead is involved, however, because we want the clone to have its own underlying storage structure that is a copy of the structure for the original collection. Let us see how the cloning mechanism is implemented in the ArrayList class. Recall that an ArrayList object has two instance variables, the integer value listSize and a reference to an array containing the list elements and additional capacity :
- private int listSize;
- private T[] listArr:
This is not quite what we want because the original and cloned variables of listArr references the same list of elements. We solve that problem by allocating in the clone an array of size listSize referenced by a new variable value of listArr, and then by copying the elements from the original list to the new list. The copy is not created with excess capacity that may have been available in the original collection :
- //replace listArr in copy by a new reference to an array
- copy.listArr = (T())new Object[listSize];
- //copy the elements from listArr to copy.listArr
- for(int i=0; i
- copy.listArr[i] = listArr[i];
沒有留言:
張貼留言