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
GraphCloneable
s). In particular, reference cycles among GraphCloneable
s 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 TypeMethodDescriptionvoid
createGraphClone
(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
GraphCloneRegistry
before this method returns. -
The registration in the previous step must occur
prior to recursing on any
GraphCloneable
fields. -
All
GraphCloneable
fields must be copied viaGraphCloneRegistry.getGraphClone(T)
.
The most efficient implementation of this method often involves declaring the class to implement
Cloneable
and starting with an invocation ofsuper.clone()
(the class need not actually overrideObject.clone()
). For that reason, this method is declared to throwCloneNotSupportedException
as a coding convenience; if aCloneNotSupportedException
is 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
- ifregistry
is nullCloneNotSupportedException
- declared so implementors can invokesuper.clone()
directly; if thrown, it will trigger aRuntimeException
-
The new clone must be registered with the provided
-