通过序列化进行深度克隆时,通过引用克隆一些实例字段

Having some instance fields cloned by reference when deep-cloning through serialization

我正按照 的建议,使用序列化深度克隆一个大的 Java class。在相应的问题中,我解释了为什么我需要以这种方式克隆,这突出了不同深度克隆技术结果的重要差异,关于在克隆中保留共享引用,这在我的例子中是必须的。简而言之,如果在原始的两个字段中指向同一个对象,那么在深度克隆中,这些字段不应该指向两个不同的新对象,而是指向同一个新对象。通过序列化深度克隆实现了这一点。

由于此技术所需的树中 classes 的唯一更改是让所有 classes 实现 Serializable,因此我不写 "clone" 方法在树的每个 class 中。所以我没有在每个 class 中编写代码来克隆它的每个字段。但我仍然想从克隆过程中 排除 一些字段,我通过将 transient 修饰符添加到我不想要的字段的声明中来实现克隆。这些字段在克隆中将是 null

现在我有不同的需求。我需要能够说某个字段确实必须被克隆,但不是深度克隆:只需复制引用;让克隆中的该字段指向与原始对象中相同的对象。

所以我想知道如何通过简单地复制引用而不是序列化来克隆特定字段,而不是像对其他字段一样反序列化它。这是我的问题。

否则我能想到的唯一解决方案是在树的每个 class 和每个 "clone" 中实现一个 "clone" 方法(不一定 Object.clone()) ] 方法显式分配每个字段,对某些字段使用序列化并复制其他字段的引用。但是除了由于 class 克隆有很多字段而需要做很多工作之外,我还担心这样我将不再保留主对象树中的共享引用,因为我会分别克隆每个字段,所以如果树中的两个字段指向同一个对象,那么在克隆每个字段时将不知道这个事实,因此序列化不可能使它们指向相同的新对象。

我想证明我在评论中提到的关于复制私有瞬态字段的想法。在这里我进一步发展了一点,引入了一个 getter 来更清楚地表明可以从对象中取出引用。请注意 getter 是私有的,也就是说,它只能从 class 内部调用,这足以满足我们的目的。代码已编译。

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

}

编辑:在评论中回答你的问题,如果需要浅拷贝的字段在你的大对象中嵌套得更深,你需要从外部调用并委托给持有该字段的子对象:

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

}

此技术可用于任何嵌套级别。每个级别都需要一个委托方法,我的口味是每个级别都有私有 getter (and/or setter)。