Interface GraphCloneable
Graph cloning creates a copy of an object graph that, like a normal deep copy, contains no references to objects in the
original object graph (except for immutables), but in addition, each GraphCloneable object is only copied once.
As a result the copy graph has the same reference topology as the original graph (with respect to all the
GraphCloneables). In particular, reference cycles among GraphCloneables are preserved and do not
cause infinite loops.
Graph cloning operates similar to a deep copy operation, except that graph cloning uses a GraphCloneRegistry to keep
track of new objects as they are created. When each object's GraphCloneable references are copied, the
GraphCloneRegistry is used to check whether the referred-to objects have already been copied, and if so, the existing
copy is used. This requires that implementations register their clones
prior to recursing on any GraphCloneable fields.
The net effect is equivalent to serializing and then deserializing the entire object graph, but without the overhead,
losing transient values, and other issues.
Here is an example of a class properly implementing this interface:
// We implement Cloneable so super.clone() will work
public class Person implements Cloneable, GraphCloneable {
// Regular fields
private int age;
private String lastName;
private String firstName;
private List<String> nicknames = new ArrayList<String>();
// GraphCloneable fields - values may be null and/or even refer back to me
private Person spouse;
private List<Person> friends = new ArrayList<Person>();
// Getters & setters go here...
// Our implementation of the GraphCloneable interface
@Override
public void createGraphClone(GraphCloneRegistry registry) throws CloneNotSupportedException {
// Create clone and register it with the registry
final Person clone = (Person)super.clone(); // this copies all of the simple fields
registry.setGraphClone(clone); // let the registry know who our clone is
// Deep copy any non-GraphCloneable fields not already handled by super.clone()
clone.nicknames = new ArrayList<String>(this.nicknames);
// Now copy GraphCloneable fields using registry.getGraphClone()
clone.spouse = registry.getGraphClone(this.spouse);
clone.friends = new ArrayList<Person>(this.friends.size());
for (Person friend : this.friends)
clone.friends.add(registry.getGraphClone(friend));
}
}
To graph clone any object graph rooted at root, you would do this:
new GraphCloneRegistry().getGraphClone(root);
- See Also:
-
Method Summary
Modifier and TypeMethodDescriptionvoidcreateGraphClone(GraphCloneRegistry registry) Create a graph clone of this instance and register it with the givenGraphCloneRegistry.
-
Method Details
-
createGraphClone
Create a graph clone of this instance and register it with the givenGraphCloneRegistry.This method should perform a normal "deep copy" operation, but with the following changes:
-
The new clone must be registered with the provided
GraphCloneRegistrybefore this method returns. -
The registration in the previous step must occur
prior to recursing on any
GraphCloneablefields. -
All
GraphCloneablefields must be copied viaGraphCloneRegistry.getGraphClone(T).
The most efficient implementation of this method often involves declaring the class to implement
Cloneableand starting with an invocation ofsuper.clone()(the class need not actually overrideObject.clone()). For that reason, this method is declared to throwCloneNotSupportedExceptionas a coding convenience; if aCloneNotSupportedExceptionis actually thrown, it will trigger aRuntimeException.This method will only be invoked once for any instance during a graph cloning operation.
- Parameters:
registry- registry for clones- Throws:
NullPointerException- ifregistryis nullCloneNotSupportedException- declared so implementors can invokesuper.clone()directly; if thrown, it will trigger aRuntimeException
-
The new clone must be registered with the provided
-