4
votes

I am deep-cloning instances of a certain class by implementing clone() in this class and in its whole hierarchy of fields that compose it. In the clone() implementations that I put in these classes, I assign each field of the new instance by calling clone() on the corresponding field of the original (this). Then I just call clone() on the main class. I believe this is quite a standard way of deep-cloning.

Below there is a small runnable example. The clone I get is a real deep copy, where the objects contained in each field and subfield are all new objects, identical to their counterparts from the original instance.

But this means that if in the original the fields a and b were referencing the same object X, in the deep clone they will not be referencing the same object (clone of X); instead they will be referencing two different clones of X.

So I would like to deep-clone an object by deep-cloning all its fields in its whole hierarchy, but if the hierarchy contains the same reference in more than one field, only one of these fields should be deep-cloned into a new object; the other fields will just reference this new object.

It doesn't look like a problem with an easy solution, however I'm wondering if some technique exists for that, or maybe some tool or library that does that.

TestClone.java

public class TestClone {

    public static void main(String[] args) throws CloneNotSupportedException {

        // Create the object to share :

        SharedObject shared = new SharedObject(1);

        // Create the object to clone, which will own two Holder instances
        // both holding a reference to *the same* object :

        MainObject original = new MainObject(new Holder(shared), new Holder(shared));

        // Show that both holders hold a reference to the same object :

        System.out.println("Original holder1 holds " + original.holder1.field.hashCode());
        System.out.println("Original holder2 holds " + original.holder2.field.hashCode());

        // Deep-clone the main object :

        MainObject cloned = (MainObject) original.clone();

        // Show that the two cloned holders now hold a reference to *different* cloned objects :

        System.err.println("Cloned   holder1 holds " + cloned.holder1.field.hashCode());
        System.err.println("Cloned   holder2 holds " + cloned.holder2.field.hashCode());

        // How to clone so that they will hold a reference to *the same* cloned object ?
    }

}

SharedObject.java

public class SharedObject implements Cloneable {

    public int n;

    public SharedObject(int n) {

        this.n = n;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {

        SharedObject clone = (SharedObject) super.clone();

        clone.n = this.n;

        return clone;
    }

}

Holder.java

public class Holder implements Cloneable {

    public SharedObject field;

    public Holder(SharedObject field) {

        this.field = field;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {

        Holder clone = (Holder) super.clone();

        clone.field = (SharedObject) this.field.clone();

        return clone;
    }

}

MainObject.java

public class MainObject implements Cloneable {

    public Holder holder1;

    public Holder holder2;

    public MainObject(Holder holder1, Holder holder2) {

        this.holder1 = holder1;

        this.holder2 = holder2;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {

        MainObject clone = (MainObject) super.clone();

        clone.holder1 = (Holder) this.holder1.clone();

        clone.holder2 = (Holder) this.holder2.clone();

        return clone;
    }   

}
2

2 Answers

2
votes

There is no "standard" way of such a clone operation. Additionally, I am not aware of any library that supports that.

What your requirement really says, is to build a 1-to-1 mapping (a bijection) from original object to a cloned object.

A technique would be to first build up such a hierarchy by cloning each object if it is not in the map, once the clone method is called on your root object. Afterwards, assemble the references in the new clone hierarchy.

That technique - by the way - is already implemented by the serialization technique in Java. Have all your classes implement Serializable, then write the root object to an ObjectOutputStream, pipe it to an ObjectInputStream, and then deserialize all objects. The serializing mechanism takes care of your requirement.

1
votes

Short answer: not possible.

Long answer:

That is the thing with deep cloning: you don't have good access to it. You see, in the end, cloning is done by the JVM (more or less: black magic).

Now you ask: how can I use that "standard" way of cloning; but somehow makes sure that the whole process of cloning some some "root object X" will use some sort of "caching" mechanism. I am note aware of any existing mechanism to instruct the JVM to do so.

Long story short: I think you have to look into other alternatives; like serializing into some reasonable format; to then work with that output.

And to finally a quote from a great answer:

It sounds like you're thinking that cloning is a good idea (vs. using a copy constructor, factories or their equivalent).

And, continued with:

Now, more importantly, cloning is a bad idea.