2

I am deep-cloning a big Java class using serialization, as suggested by this answer. In the corresponding question I explained why I need to clone in this way, and that highlights an important difference in the results of different deep-cloning techniques, about preserving shared references in the clone, which in my case is a must. In a few words if in the original two fields point to the same object, in the deep clone those fields should not point to two different new objects but to the same new object. Deep cloning through serialization achieves this.

Since the only change to the classes in the tree that is required by this technique is to have all the classes implement Serializable, I am not writing a "clone" method in each class of the tree. So I am not writing in each class the code to clone each of its fields. But I still want to exclude some of the fields from the cloning process, and I do that by adding the transient modifier to the declaration of the fields that I don't want to clone. Those fields will be null in the clone.

Now I have a different need. I need to be able to say that a certain field does have to be cloned, but not deep-cloned: just copy the reference; let that field in the clone point to the same object as in the original.

So I am wondering how to make so that serialization will clone that particular field by simply copying the reference instead of serializing - deserializing it like it does with the other fields. This is my question.

Otherwise the only solution I can think of is to implement a "clone" method (not necessarily Object.clone()) in each class of the tree, and in each "clone" method assign each field explicitly, using serialization for some fields and copying the reference for other fields. But in addition to this being a lot of work due to the class to clone having lots of fields, I'm also afraid that in this way I will no longer be preserving the shared references within the tree of the main object, because I would be cloning each field separately, so if two fields in the tree point to the same object this fact will not be known while cloning each of these fields, so it won't be possible for serialization to make them point to the same new object.

Community
  • 1
  • 1
SantiBailors
  • 1,596
  • 3
  • 21
  • 44
  • 1
    An idea would be to declare that field transient and then copy the reference after the cloning through serialization. Also I believe serialization of enums take into account that there can only be one instance of each, so if you could have the type that field referes to be en enum? – Ole V.V. Sep 03 '16 at 11:57
  • @OleV.V. If I understand correctly, I'd still create the deep clone _of the whole root object_,but then reassign some of its `transient` fields to the same reference as in the original. One problem is that some fields might be not `public` or with a setter. Also one reason why I want not to deep-clone some fields is that they are large lists which I don't need to deep-clone so I want to exclude them from the deep cloning for performance. The `Enum` point is interesting,I could make some of the fields `Enum` but for some other it would be too much of a hack that would make the code too unclear. – SantiBailors Sep 03 '16 at 12:24
  • @OleV.V. Sorry, I had misunderstood part of your idea, so the disadvantage I mentioned in my comment about performance does not exist, as you meant to make those fields transient and assign them after cloning, so they would not get deep-cloned. I'm actually looking into whether I can get access to all those fields from the place where I clone the main object, so I could set those fields to the original reference after cloning; that might actually solve my problem. – SantiBailors Sep 03 '16 at 12:52
  • In case of lack of a public getter, please note that private field are only class private, not object private. So an object can copy a reference from a private field of an object belonging to the same class. You may feel that it’s not the most beautiful solution, but it’s an option. – Ole V.V. Sep 03 '16 at 13:23

1 Answers1

2

I want to demonstrate the idea I mentioned in a comment about copying a private transient field. Here I’ve developed it just a little further, introducing a getter to make it clearer that the reference can be taken out of the object. Notice that the getter is private, that is, it can only be called from within the class, which is enough for our purpose. The code has been compiled.

public class MyClonable {

    private transient List<String> fieldRequiringShallowCopy;

    /** getter to be used by cloning */
    private List<String> getFieldRequiringShallowCopy() {
        return fieldRequiringShallowCopy;
    }

    /**
     * Call this method after cloning by serialization to copy transient field.
     * @param original The object that this object was cloned from.
     */
    public void finishClone(MyClonable original) {
        this.fieldRequiringShallowCopy = original.getFieldRequiringShallowCopy();
    }

}

Edit: To answer your question in the comment, if the field requiring shallow copy is more deeply nested inside your big object, you need to call from the outside and delegate to the subobject holding that field:

public class BigClonable {

    private Part enclosedThing;

    private Part getEnclosedThing() {
        return enclosedThing;
    }

    /**
     * Call this method after cloning by serialization to copy transient field.
     * @param original The object that this object was cloned from.
     */
    public void finishClone(BigClonable original) {
        // delegate to part object
        enclosedThing.finishClone(original.getEnclosedThing());
    }

}

public class Part {

    private transient List<String> fieldRequiringShallowCopy;

    /** getter to be used by cloning */
    private List<String> getFieldRequiringShallowCopy() {
        return fieldRequiringShallowCopy;
    }

    public void finishClone(Part original) {
        this.fieldRequiringShallowCopy = original.getFieldRequiringShallowCopy();
    }

}

This technique can be used to any nesting level. You need a delegating method on each level, and my taste would be for a private getter (and/or setter) at each level too.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • What if the private field requiring shallow copy is not a direct field of the root object but a field of a field ? F.ex. the root object `MyCloneable` has a field `a` of type `A` which has a `private transient` field `b` of type `B` which is the one requiring shallow copy. From `MyCloneable` I can access `this.a` but not `this.a.b`. Should I also add to `A` a `public void finishClone(A original)` and call it from `MyCloneable.finishClone` ? – SantiBailors Sep 03 '16 at 14:53
  • It seems a good solution for my need, thanks, I will use this design. The ideal would of course have been to be able to simply mark the fields to be shallow-copied in their declaration (without having to use a library for that), but with this design I just need to make them `transient` and assign them in a `finishClone` method to be created in each class having such fields, and I can keep using `SerializationUtils.clone(*)`. I will just do without having a getter for each field requiring shallow copy and from `finishClone` I will access the private field instead of calling the private getter. – SantiBailors Sep 04 '16 at 08:13