Effective JavaEffective cloning
- By Steve Ball
- January 18, 2000
THE
CLONE METHOD of the
Object class is a somewhat magical method that does what no pure Java method could ever do: It produces an identical copy of its object. It has been present in the primordial
Object superclass since the Beta-release days of the Java compiler*; and it, like all ancient magic, requires the appropriate incantation to prevent the spell from unexpectedly backfiring. In this case, the "open sesame" to effective cloning is "
Cloneable," a talisman whose name activates the power of the
clone method.
Unfortunately, the magic that wrought clone and Cloneable into existence was spelled imperfectly and the method and interface it created have numerous defects.Ý
I'll show you how the clone method is implemented within the Java virtual machine (JVM) and highlight its special relationship with the Cloneable interface. I'll also draw your attention to the problems that typically arise when attempting to make effective use of the cloning services and how these difficulties may be overcome when writing your own clone methods.
Maxim 18: Clone Objects Passed Between Classes to Protect Encapsulation
It is Java's shallow copy semantics that make cloning-the process of instantiating a new object that is a duplicate of another-of such importance in Java. When an object is passed as a method argument, both the called and the calling methods share references to the same actual instance.1
class GraphPoint {
GraphPoint(Point c, int t) {
center = c;
type = t;
}
static final int CROSSHAIR = 0;
static final int DIAMOND = 1;
private Point center;
private int type;
}
As the GraphPoint constructor merely records a reference to the supplied Point object, it is vulnerable to changes being made to that object by the caller:
void plot_line(GraphPoint[] line, int m, int c) {
Point next = new Point(0, c);
while (next.x < line.length)="" {="" line[next.x]="new" graphpoint(="" next,="" graphpoint.crosshair);="" next.x="" +="1;" next.y="" +="m;" }="">
This does not fill the array with a series of graph points placed on the line y=mx+c but, instead, stacks all the points on top of each other at the position of the last one constructed! All GraphPoint objects simply refer to next's Point instance.
For some classes, it is meaningful for an object to remain associated with the specific instances it had passed to its constructors; where it is not, either the caller is required to pass only distinct instances as arguments:
line[next.x] = new GraphPoint(new Point(
next), GraphPoint.CROSSHAIR);
or the class must take steps to encapsulate itself from modifications affected without:
GraphPoint(Point c, int r) {
center = new Point(c);
radius = r;
}
If resolved within the class, the same problem must be addressed in any mutators also:
void setCenter(Point c) {
center = new Point(c);
}
For simple concrete types like GraphPoint, holding references to unique cloned instances is usually appropriate.ý
Maxim 19: Prefer the clone Method To Other Interfaces for Providing Cloning Support
The Point class is one of a small number of classes in the Java API that provides cloning support in the form of a constructor that takes a single argument of its own type (String and java.awt.Rectangle are two others). The disadvantage of this approach, compared to the clone method, is that it may not be accessed polymorphically and is usually less efficient. Some classes, such as java.util.Date, require even weirder syntax to clone their instances:
Date clone = new Date(birthdate.getTime());
Such alternatives are best avoided in favor of the standardized clone method. In addition to allowing an object to be cloned without knowing its runtime type and being to quicker execute, it does not require users of your class to remember that the class presents its cloning function in a non-standard way. Had the Point class defined a clone method, its instances could be cloned the same way as those of any other class:
line[next.x] = new GraphPoint((
Point) next.clone(), GraphPoint.CROSSHAIR);
A second common scenario, where a class depends on being able to clone instances of helper classes appearing in its public interface, arises in accessor methods:
public class BankAccount {
public Currency getBalance() {
return balance;
}
public boolean deposit(Currency amount) {
//...
}
private Currency balance;
}
Because the getBalance method returns a reference to an instance referred to by one of its own fields, it can do nothing to prevent perversions of its own encapsulation. The protection the class provides is easily circumvented:
BankAccount sxb = bank.getAccount("Steve Ball");
sxb.getBalance().setValue(1e9, "USD");
Any security and double-entry bookkeeping checks made in the BankAccount's deposit method have been successfully bypassed. To prevent callers from assigning themselves instant billionaire status in this fashion, the BankAccount class needs to ensure that its private members are indeed kept private§:
public Currency getBalance() {
return (Currency) balance.clone();
}
If it is conceivable that objects of your classes will be passed as arguments to constructors or mutator methods or be returned from accessor methods, then it is likely, at some point, that someone is going to want to isolate their class instances by cloning yours.
With simple classes such as these, the need for cloning is readily apparent, but not all occurrences of this type of entanglement are as easy to identify; inexplicable bugs in far-off parts of the program are more often the symptom that necessary cloning was overlooked. Furthermore, providing deep copy cloning for container classes is predicated on the elements of the container providing cloning support themselves, via the clone method.
The clone method, then, is an important part of a class's interface to the rest of the system. Fortunately, much of the work required to write a fully-functioning clone method has already been supplied in the form of the Object.clone method. Adding cloning support to a class merely involves overriding and augmenting this method.
One Size Fits All
The clone method of the Object class is necessarily declared as a native method, its implementation residing within the JVM:
protected native Object clone() throws
CloneNotSupportedException;
The method is implemented in the compiled language of the JVM and linked into JVM executable. In pseudo-code it reads like this:
If this object implements the Cloneable interface
Construct a new object of the same runtime type.
Copy the in-memory image of this object into the new one.
else, if this object is an array
Construct a new array of the same type and size.
Copy the in-memory image of this array into the new one.
else
Construct and throw a new CloneNotSupportedException object.
Note that this method will only clone objects whose classes implement the Cloneable interface#; attempts to clone objects of other classes are rejected with an exception. The Cloneable interface is part of the Java API, residing in the java.lang package with the following definition.
public interface Cloneable {
}
The interface declares no methods; it is simply used as a marker interface to enable cloning for selected classes. The requirement that a clonable class must possess Cloneable as one of its superinterfaces acts as a safeguard for classes that cannot validly be cloned in this way.
But why would you want to prevent a class's objects from being cloned? After all, it sounds like a useful ability. The answer is that the default operation of the Object.clone method may produce too shallow a copy for some classes.|| Such classes need special handling to make a more distinct separation between the original and cloned objects for the cloning operation to produce valid clones. Once this special handling has been implemented, cloning may be safely enabled by implementation of the Cloneable interface.
Booby Trapped Clones
This scenario can be demonstrated with a simple (non-clonable) stack class.
public class Stack {
public Stack() { this(32); }
public Stack(int sz) {
data = new Object[maxSize = sz];
}
public void push(Object o) {
if (size < maxsize)="" data[size++]="o;" }="">
public Object pop() {
return size == 0 ? null : data[-size];
}
private int maxSize, size = 0;
private Object[] data;
}
To add cloning support to the Stack class, two simple steps are required: implementing the Cloneable interface:**
public class Stack implements Cloneable {
and overriding the clone method with a public definition:
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(e.toString());
}
}
The Stack class's clone method delegates to the clone method of its Object superclass to perform the cloning operation. (It can also safely absorb the redundant CloneNotSupportedException exception, which it knows won't ever be thrown-more on that later on.)
If this method is adding nothing to the functionality of the method it inherited from its superclass, why bother to override it at all? The clone method declared in the Object class has protected accessibility; to allow methods of classes other than Stack and its subclasses to clone Stack instances, the method must be promoted to public accessibility. The Stack class must also implement the Cloneable interface to enable the Object.clone method to proceed with the cloning.
Calling a stack's clone method produces a new stack identical to the first:
Stack crockery = new Stack();
crockery.push(new Cup());
Stack clone = (Stack) crockery.clone();
// both stacks contain one Cup object
Unfortunately, the cloned instance is rather more identical than we'd like. Although the clone operation appears to have been successful, there is a bug waiting to happen here. But it is not until new objects are pushed onto the stacks that the bug manifests:
// both stacks contain one Cup object
crockery.push(new Plate());
clone.push(new Saucer());
// both stacks contain one Cup and one Saucer!
The symptoms of this bug are typical of those introduced by faulty clone methods. They can be notoriously subtle and time-consuming to locate. This is particularly true when the faulty class is buried deep in a class hierarchy and the symptoms are only present in classes further down in the inheritance chain. As a matter of defensive programming, it is time well spent to ensure that a class's clone method will not be the source of surprises and frustration later on in the system's development.
In the case of the Stack class, the problem occurred because the array (data) within the Stack object was duplicated by field assignment-both stacks share the same storage for their elements (but independent counts of the number of elements within it). Pushing a Saucer object onto the cloned stack pushed the Plate object on the original off the stack and into thin air!
Maxim 20: Ensure That Your Clone Methods Perform a Deep Enough Copy
To produce more distinct cloned objects, the array instance containing the stack's elements must also be cloned. To do this, the class must augment the clone method it inherits from its superclass with some special handling for this particular instance member.
public Object clone() {
try {
Stack clone = (Stack) super.clone();
clone.data = (Object[]) clone.data.clone();
return clone;
} catch (CloneNotSupportedException e) {
throw new InternalError(e.toString());
}
}
Recalling that all arrays implicitly implement Cloneable, we can clone the array by calling its clone method, producing a new array instance with the same element values. The other members of the class do not need to be explicitly copied from the original stack into this object-that task was accomplished by the call to the superclass's clone method.
Because the array instances are now distinct, elements may be pushed and popped onto either of the two stacks without the operations affecting the other. By performing a deeper copy than the default one performed by the Object class's clone method, the clone has been given enough separation from the original to eliminate the bug:
// both stacks contain one Cup object
crockery.push(new Plate());
// one Cup and one Plate
clone.push(new Saucer());
// one Cup and one Saucer
This is a deep enough copy for most purposes, but it is not a true deep copy. A cloning operation that produced truly distinct Stack clones would also clone the elements of the stack. As with all the container classes from the Java API, the clones produced by Stack's clone method share references to the contained elements.
Popping a Cup object off one stack and modifying some aspect of its appearance also modifies the Cup object still on the other stack. This may be what the designer of the Stack class intended, but often this behavior comes as an initial surprise to users of Java's container classes. The deep copy cloning of generic Object-based container classes presents some challenges because the clone method of a class-even when overridden as a public method in its runtime type-is inaccessible through an Object reference where its static type (Object) declares it as protected:
public Object clone() {
try {
Stack clone = (Stack) super.clone();
clone.data = (Object[]) clone.data.clone();
for (int i = 0; i < clone.data.length;="" ++i)="" next="" line="" will="" not="" compile="" clone.data[i]="clone.data[i].clone();" return="" clone;="" }="" catch="" (clonenotsupportedexception="" e)="" {="" throw="" new="" internalerror(e.tostring());="" }="">
As a rule of thumb, if a class contains fields of reference types, the overriding clone method will probably require the addition of special handling to produce clones of adequate independence from their progenitors. An exemption applies for references to instances of final immutable classes (String, Color, and Integer objects, for example). Because they cannot have their states altered after construction, it is safe for their instances to be shared by multiple objects.
The crockery-stacking example is one where a method possesses a reference to an object (the crockery stack object) and wishes to clone it. However, there is another situation where this is not the case, but nonetheless, it is imperative that classes provide suitable cloning support; even if the clone method is not publicly accessible!
Maxim 21: Remember That Subclasses Depend On a Class's Clone Method Too
Subclasses wishing to support cloning rely on their superclasses emitting valid clones from their clone methods. Consequently, classes that do not provide a publicly accessible clone method or implement the Cloneable interface must still provide a reliable implementation of clone if field-by-field assignment would be incorrect!
If defined in such a class, the clone method is provided solely for the benefit of subclasses. To prevent other classes from gaining access to the method, the overriding clone method may be declared protected. The class should still implement the Cloneable interface though, as the interface is an integral part of the implementation of the class's clone method: Without it, clone will fail.
public class Stack implements Cloneable {
// may only be cloned by subclasses
protected Object clone() {
// as before...
}
}
public class PrintQueue extends Stack {
// may be cloned by any class
public Object clone() {
return super.clone();
}
// other methods...
}
If it would be nonsensical or impossible for a class to be safely cloned (perhaps because its superclass does not provide a reliable clone method), the class could prevent all classes from cloning its instances by declaring a final clone method that throws an exception. This takes the approach that a class that cannot be cloned safely should not be cloned at all; users are protected from unwittingly obtaining a booby-trapped clone object.
public class GlobalConfigSettings {
// may not be cloned at all
final protected Object clone() throws
CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}
Of course, if a superclass has already declared a clone method with no checked exceptions, then this may be difficult.
Java API Example
When designing a new class, it is easy to forget to provide a reliable clone method if you do not intend to allow the class to be directly clonable, forgetting that subclasses depend on their superclasses for cloning support too. It is also easy for this mistake to go unnoticed in testing, as the impact will not become apparent until the class is extended.
A case in point is the java.util.Observable class from the Java API. The nature of the Observable class means that it must always be extended to be of any use. However, it has no subclasses within the Java API itself, so it was not until Java developers tried cloning subclasses of Observable that its failure to provide a reliable clone method highlighted the lack of subclasses.
A class that extends Observable can declare an overridden public clone method that contains any required special handling for the fields of the subclass. However, Observable does not define a clone method itself, so the imbedded Observable object will be booby-trapped. This cannot be made to work:
public class Stack extends Observable implements Cloneable {
// as before...
public Object clone() {
try {
Stack clone = (Stack) super.clone();
// clone already corrupt
// as before...
}
}
In a parallel to the crockery-stacking bug, the fundamental problem is that the Observable class contains a reference to a vector of Observer objects that is cloned by default field assignment. If observers are added or deleted to any of the cloned objects, all objects with that family tie undergo the same changes. To make matters worse, the fix-up for the Observable class cannot be performed in the subclass's clone method because the subclass field in question (a Vector named obs) is private and inaccessible. The unfortunate consequence is that it is not possible to construct a class that is both observable and clonable.
This is a problem that could easily have been avoided and is now very difficult to remedy. In a class designed to be extended, an overriding clone method is an obvious omission. Because it would hardly be useful to clone an Observable object that has not been subclassed, this problem did not present itself until the Java API was released to developers as a defined part of the language.
Maxim 22: Provide Cloning Support for Every Class
Those classes that depend on some class X supporting cloning include:
1. Any class that wants to accept an X as an argument to a constructor or mutator method and wishes to retain a private (unique) copy.
2. Any class that wants to return an X from an accessor method without allowing callers direct access to the public methods of the instance referred to by its own member field.
3. Any class which subclasses X and wishes to provide cloning support itself.
In small systems, it is often feasible to know in advance all the uses that a new class will be put to. This is rarely the case with large systems development. For large-scale development of robust systems, it makes more sense to supply cloning support when the class is initially developed.
There are so few cases where the time spent providing cloning support would not be justified, that it is easier to list the conditions where an overridden clone method is not necessary than to list the ones where it is:
1. Where a class contains no member fields that require special handling and either inherits a public clone method or inherits a protected clone method and should only be clonable by classes that extend it.
2. Where a class is final (i.e., cannot be subclassed) and has inherited a clone method with protected accessibility.
The first situation often crops up when a class is being subclassed to add a simple ability but no additional state information:
public SortableVector extends Vector implements Sortable {
public void sort() {
Algorithms.quicksort(elementData, elementCount);
++modCount; // invalidate iterators
}
}
The second case is usual for anonymous classes, which are implicitly final and often do not inherit a public clone method that other classes could access.
Outside of anonymous classes, nested classes declared private, and those whose fields are all inherited, very few other classes will be robust and complete without a clone method. Think twice before designing a class to have no clone method at all.
Powerful Magic Imperfectly Cast
The cloning spell invoked by calling the clone method is a powerful magic, summoning new life out of the void. It is also efficient, regular, polymorphic, and extensible. Impressive and powerful as cloning wizardry is, it is built on a shaky foundation: the Object.clone method. It is a disenchanting reality that the cloning mechanism in Java is flawed; providing cloning support is needlessly clumsy.
For example, clone methods are required to absorb or propagate an exception thrown by Object.clone even when the class containing the clone method itself implements the Cloneable interface. This is a minor nuisance; but when a class extends one that supports cloning (and therefore has no checked exceptions) and needs to be able to disable cloning of its own instances, the developer is forced to search for a workaround.
This problem can be easily remedied, but not all of clone's problems have simple solutions-in the presence of final members and inner classes, providing a working clone method is something of a black art.
These problems in no way detract from the clone method's usefulness though-it has a critical role to play in the development of extensible and robust systems. Furthermore, the hard work of providing cloning support has already been provided in a method inherited by every class. Only a few additional (though carefully chosen) incantations are required to imbue your own classes with an impressive ability that can greatly enhance their flexibility and usefulness to other classes in the system. n
References
1. Ball, S. and J. M. Crawford, "Assignment Surprises," Java Report, Vol. 3, No. 7, July 1998, pp. 14-18.
2. Source code for the example classes is available at effectivejava.com/column/e-cloning.
FOOTNOTES
* Its close cousin, the copy method, did not fare as well. It was removed from the Object class in the 1.0 release of the Java API. This method performed the useful function of taking a reference to an object of the same type and performing field-by-field assignment from its argument to the current object. In fact, clone was originally implemented by calling copy with an argument of this against a new blank instance. Sadly, its magic was deemed too dangerous and wonderful and the method was banished. There now exists no standardized approach for assigning one object's state to another.
Ý These defects include a spelling error in the interface name-clonable is correctly spelled with only one "e".
ý The question of how this is best achieved is a tradeoff between simplicity and efficiency. Placing the responsibility on users for passing only unique instances to a class, shoulders them with an avoidable burden, but eliminates unnecessary cloning when the instances are known to be unique in the calling function.
§ An alternative solution would be to make Currency objects immutable. If the objects could not have their values altered after construction, they could be freely passed into and out of methods without encountering any of these problems. This is often a reasonable design decision for simple concrete types. Or, more elaborately, the class could be split into a superclass (ConstCurrency) containing only accessor methods and a subclass (Currency) containing the mutating methods-the getBalance method of the BankAccount class could then be modified to have the return type ConstCurrency.
# As this pseudo-code implies, all arrays implicitly implement the Cloneable interface and, so, may be cloned with the clone method. In contrast to other (non-array) classes, their clone methods are accessible as public methods-which they would have to be, as non-array classes may not explicitly subclass array types.
|| There may be other reasons too; some design patterns such as Factory (which requires that all instantiation of a particular type be directed through an intermediate broker) and Singleton (which requires that the number of instantiations is limited to a maximum of one) may restrict objects from being freely duplicated. For other types, supporting cloning may prohibit some efficient implementations or simply be nonsensical.
** "Implementing the interface" has an unusual meaning for marker interfaces such as Cloneable. As they declare no methods, the implementing classes are not required to override any inherited methods in order to avoid becoming abstract. Implementing the Cloneable interface entails no more than adding Cloneable to the class's implements clause. |