通过收集字段进行深度克隆

Deep clone by collecting the fields

我有 类 旨在包含许多不同类型的属性。我想自动深度克隆所有这些而不是为每个编写指令:

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;
    }
}

我想过每次都在 table 中添加字段,但我无法更改字段的引用:

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;
    }
}

我还认为我可以 return add() 方法中的包装器,但它会引入一个额外的方法 get() 以在每次我想访问属性时调用:

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

有没有一种方法可以将字段更改为源,就像我们可以在 C 中使用指针实现那样?

注意:我已经检查过 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

编辑:我不使用序列化的原因之一是事实上,我有一个最终的属性,我只想要一个新的实例。

我想过让它成为瞬态对象,然后给它一个新对象,但我不能,因为它是最终的:

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;
    }
}

写文章(或问题asked/answered)时一定要检查,否则你会产生很多误解。我在评论中引用的文字位于页面底部。如果您没有看到它,请使用浏览器的 "Find in page" 功能。

序列化和反序列化final字段也不是问题。你不能这样写:a.b = new B(); // Error, b is final,但你不需要。序列化是在 JVM 级别实现的,它可以执行很多技巧,例如在不调用任何构造函数的情况下创建对象。

最后,我找到的唯一不需要更改最终属性的答案是在 class DeepCopy 中更改 http://javatechniques.com/blog/faster-deep-copies-of-java-objects/ ObjectOutputStream 方法以替换 class 类型的属性有新的。

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();