-1

I have classes which aim to contain many attributes of different types. I would want to automatically deep clone all of them rather than writing an instruction for each of them:

class AttributesContainer implements Cloneable {
    Type1 a1 = new Type1(...), a2 = new Type1(...);
    Type2 b1 = new Type2(...);
    ...

    public AttributesContainer clone() {
        AttributesContainer ac = (AttributesContainer) super.clone();

        // Regroup that like using a loop
        ac.a1 = a1.clone();
        ac.a2 = a2.clone();
        ac.b1 = b1.clone();
        ...

        return ac;
    }
}

I have thought about adding the fields in a table each time but I could not change the reference of the fields:

class ContainerAbstract implements Cloneable {
    public <T> T add(T t) {
        // adds the reference into a list
        return t;
    }

    public ContainerAbstract clone() {
        ContainerAbstract ca = (ContainerAbstract) super.clone();

        // copy the attributes

        return ca;
    }
}

class AttributesContainer extends ContainerAbstract implements Cloneable {
    Type1 a1 = add(new Type1(...)), a2 = add(new Type1(...));
    Type2 b1 = add(new Type2(...));
    ...

    public AttributesContainer clone() {
        AttributesContainer ac = (AttributesContainer) super.clone();
        return ac;
    }
}

I have also thought that I could return a wrapper in the add() method but it would introduce an extra method get() to call each time I want to access an attribute:

AttributesContainer ac = new AttributesContainer();
ac.get()...;

It there a way to change the fields to there source, like we could achieve that in C using a pointer?

NB: I already checked Copy fields between similar classes in java, How do you make a deep copy of an object in Java? and http://www.java2s.com/Tutorial/Java/0125__Reflection/Returnalistofallfieldswhateveraccessstatusandonwhateversuperclasstheyweredefinedthatcanbefoundonthisclass.htm.

EDIT: One of the reasons I don't use the serialization is that in fact, I have a final property for which I just want a fresh new instance.

I thought about making it transient and then giving it a new object but I can't, since it is final:

class A {
    private Double d = new Double(2);
    public final transient B b = new B();

    public A copy() {
        A a = (A) DeepCopy.copy(this);
        a.b = new B(); // Error, b is final
        return a;
    }
}
Machavity
  • 30,841
  • 27
  • 92
  • 100
Codoscope
  • 892
  • 1
  • 10
  • 18
  • Sorry, I didn't realize that you had already checked the question I marked as a duplicate. That question explains the best way to perform deep copy, so why doesn't serialization work for you? – Kayaman Aug 25 '15 at 09:36
  • I read that it was a slow and artificial solution but that could be used in the general case including difficulties like private fields in the superclass. Since I don't have those difficulties, I'm searching for a better solution. – Codoscope Aug 25 '15 at 09:48
  • It's artificial in the sense that it wasn't exactly meant to be used that way, but since `clone()` is an even bigger kludge, there's no reason to go that way. I'm highly dubious about slowness, especially since you haven't tested it (and the post doesn't mention anything like that). You'll be looking for a better solution for a long time. – Kayaman Aug 25 '15 at 09:53
  • The link http://javatechniques.com/blog/faster-deep-copies-of-java-objects/ in the post speaks about its slowness. But even though, it doesn't solve my problem since I have a final attribute for which I just want a new empty object. I'm to add this point in my question. – Codoscope Aug 25 '15 at 12:24
  • The link also uses `Vector`, and in the end says: `For example, the code as shown in Figure 7 (on a 500Mhz G3 Macintosh iBook running OSX 10.3 and Java 1.4.1)`, so you might want to see when things were written the next time you make claims. Or are you perhaps running on Java 1.4? – Kayaman Aug 25 '15 at 12:26
  • I can't find this quote. Where did you find it? No, I have a quite recent Java machine. Anyway, I added into my first post a further problem unsolved by the serialization. – Codoscope Aug 25 '15 at 12:37

2 Answers2

0

Always check when articles are written (or questions are asked/answered), otherwise you'll get plenty of misconceptions. The text I quoted in my comment is in the bottom of the page. If you don't see it, use the "Find in page" functionality of your browser.

Serializing and deserializing final fields is not a problem either. You can't write this: a.b = new B(); // Error, b is final, but you don't need to. Serialization is implemented on the JVM level and it can perform plenty of tricks, such as creating objects without calling any constructors.

Kayaman
  • 72,141
  • 5
  • 83
  • 121
  • _Mea culpa_, I searched in the wrong file. I already looked at many sources before asking this question. - I know that serializing a final field causes no problem, but I don't want this field to be serialized. I just want that b receives a new instance, not the previous one nor a copy of it. With transient, the old instance is not used but I get a null instead of a new instance. Hoping it is clearer now. I checked http://www.jmdoudoux.fr/java/dej/chap-serialisation.htm and didn't find a trick for my problem. – Codoscope Aug 25 '15 at 14:03
  • See [here](http://www.oracle.com/technetwork/articles/java/javaserial-1536170.html) for info on how to use `readObject()/writeObject()`, `readReplace()` and the `readExternal()/writeExternal()` methods to fine tune the serialization process (that link is from 2000, but the serialization mechanism hasn't changed since then). There are plenty of ways to customise the process for situations such as the new final field object. – Kayaman Aug 25 '15 at 14:11
  • I checked again `readObjectNoData()`. http://stackoverflow.com/questions/7445217/java-when-to-add-readobjectnodata-during-serialization tells that the initialization can be replaced. I also checked the doc and other sources but could find no example doing so. I wonder how it could be possible since the method gets and returns void. If it is by instructions, it does not apply to my case because of the final field. I also checked again `readResolve()` but it replaces the entire object instead of a single chosen field. – Codoscope Aug 25 '15 at 16:27
  • If the final field is your only problem left, you can always initialize it with reflection. – Kayaman Aug 26 '15 at 05:25
  • Oracle itself says it's a good reason to do so in this case (http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.5.3) but I found an other solution as I wanted those fields to be automatically initialized (I have many of them). But thanks for your help! – Codoscope Sep 08 '15 at 14:05
0

Finally, the only answer I found without needing to change a final attribute is to change in the class DeepCopy in http://javatechniques.com/blog/faster-deep-copies-of-java-objects/ the ObjectOutputStream method to replace attributes of the class Type with new ones.

ObjectOutputStream out = new ObjectOutputStream(fbos) {
    {
        enableReplaceObject(true);
    }

    @Override
    protected Object replaceObject(Object arg0) throws IOException {
        // TODO Auto-generated method stub
        if(arg0 instanceof Type) {
            for(Constructor<?> constructor : arg0.getClass().getConstructors()) {
                Class<?>[] parameterTypes = constructor.getParameterTypes();
                if(parameterTypes.length == 0 /* Number of arguments in the constructor of new Type(...) */)
                    try {
                        return constructor.newInstance(/* Arguments to the constructor */);
                    } catch (InstantiationException | IllegalAccessException
                            | IllegalArgumentException
                            | InvocationTargetException e) {
                        e.printStackTrace();
                    }
            }

            try {
                throw new Exception("The constructor needed to create a new Type was not found.");
            } catch(Exception e) {
                e.printStackTrace();
            }

            return null;

        } else {
            return super.replaceObject(arg0);
        }
    }
};
out.writeObject(orig);
out.flush();
out.close();
Codoscope
  • 892
  • 1
  • 10
  • 18