5

我正在尝试复制一个对象,然后将对其进行修改,而不更改原始对象。

我找到了这个解决方案,似乎最好的方法是复制构造函数——据我了解,这会给我一个深层副本(与原始对象完全分开的对象)。

所以我试过了。但是,我注意到当下面的代码执行时,它会影响之前复制它的所有对象。当我调用 时surveyCopy.take(),这将更改 内部的值Survey,它也会更改 selectedSurvey 内部的值。

public class MainDriver {
...
//Code that is supposed to create the copy
case "11":  selectedSurvey = retrieveBlankSurvey(currentSurveys);
            Survey surveyCopy = new Survey(selectedSurvey);
            surveyCopy.take(consoleIO);
            currentSurveys.add(surveyCopy);
            break;
}

这是我的复制构造函数的代码:

public class Survey implements Serializable
{
    ArrayList<Question> questionList;
    int numQuestions;
    String taker;
    String surveyName;
    boolean isTaken;

    //Copy constructor
    public Survey(Survey incoming)
    {
        this.taker = incoming.getTaker();
        this.numQuestions = incoming.getNumQuestions();
        this.questionList = incoming.getQuestionList();
        this.surveyName = incoming.getSurveyName();
        this.isTaken = incoming.isTaken();
    }
}

那么究竟是什么问题呢?复制构造函数不能那样工作吗?我编码的方式错了吗?

4

4 回答 4

14

这是您的复制构造函数中的问题:

this.questionList = incoming.getQuestionList();

那只是复制对列表的引用。两个对象仍将引用同一个对象。

您可以使用:

this.questionList = new ArrayList<Question>(incoming.getQuestionList());

创建原始列表的副本 - 但如果它本身是可变的,这仍然不够好。Question在这种情况下,您必须创建每个Question对象的副本以实现完全隔离。

您的其他字段没问题,因为它们是原语或引用String(这是不可变的,允许您安全地共享引用)。

于 2012-12-06T14:35:26.907 回答
8

这个

this.questionList = incoming.getQuestionList();

最有可能复制对原始列表的引用(我说可能是因为它可能getQuestionList()会给您一个防御性副本)。您可能必须制作该列表的新副本。也许是包含的Question对象。也许他们提到的任何东西。

这就是深拷贝的问题。为了可靠地做到这一点,您必须复制所有可变对象。请注意,如果一个对象是不可变的(例如字符串),那么它们就无法更改,因此您可以参考原始对象,确信它们不会被更改。这同样适用于原语。鼓励代码库中不变性的一个很好的理由。

如果您无法创建不可变类,请编写您的类,使其生成防御性副本。即当客户要求它收集时,它应该制作一个副本并返回它。否则,您所谓的善意客户可能会改变您的内部状态(无意或无意)。

于 2012-12-06T14:34:48.793 回答
5

创建深拷贝时的问题是,所有不是原始类型的东西都是通过引用复制的,除非您也在其上使用特定的深拷贝构造函数。

bool在您的特定情况下,您对,intString变量没有问题,因为您通过值传递它们(实际上String是通过引用传递,但它是不可变的,因此没有问题)但是您传递的是ArrayList<Question> questionList. 当你这样做

this.object = incoming.object

你只是复制一个参考。所以这两个变量都指向内存中的同一个对象,所以你没有深度复制它。您必须创建具有相同内部值的对象的另一个实例,然后您将确定,例如this.object = new YourObject(incoming.object).

请注意,这通常意味着您的类在组合树中越复杂,您将不得不深入研究变量,直到您将它们全部复制。

于 2012-12-06T14:40:15.640 回答
0

如果我们需要复制一个简单的 pojo(不是嵌套的)。那么浅拷贝就足够了。

克隆类

导入 java.lang.reflect.Field;

public class Cloner {
    public static <T> T cloneShallow(T srcEntity, T destEntity){
        try {
            return copy(srcEntity, destEntity);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    private static <T> T copy(T srcEntity, T destEntity) throws IllegalAccessException, InstantiationException {
        if(srcEntity == null){
            return null;
        }

        Class<?> clazz = srcEntity.getClass();

        T newEntity;

        if(destEntity != null){
            newEntity = destEntity;
        }else{
            //create new instance
            newEntity = (T) srcEntity.getClass().newInstance();
        }

        while (clazz != null) {
            copyFields(srcEntity, newEntity, clazz);
            clazz = clazz.getSuperclass();
        }

        return newEntity;
    }

    private static  <T> T copyFields(T entity, T newEntity, Class<?> clazz) throws IllegalAccessException {
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            field.set(newEntity, field.get(entity));
        }
        return newEntity;
    }
}

打电话吧。。

eg.
Apple apple = new Apple();
apple.setColor("Green");

Apple newApple = Cloner.cloneShallow(apple, new Apple());
( or )
Apple newApple = Cloner.cloneShallow(apple, null);
于 2019-06-01T12:54:41.197 回答