我正在尝试复制一个Object,然后修改它,而不更改原始对象。
我找到了this solution,似乎最好的方法是复制构造函数 - 根据我的理解,这会给我一个深层复制(与原始文件完全独立的对象)。
所以我试过了。但是,我注意到,当执行以下代码时,它会影响复制它的所有先前对象。当我调用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();
}
}
究竟是什么问题?复制构造函数不是那样工作的吗?我编码错误的方式是什么?
答案 0 :(得分:13)
这是你的拷贝构造函数中的问题:
this.questionList = incoming.getQuestionList();
这只是将引用复制到列表中。两个对象仍然会引用同一个对象。
您可以使用:
this.questionList = new ArrayList<Question>(incoming.getQuestionList());
创建原始列表的副本 - 但如果Question
本身是可变的,那么仍还不够好。在这种情况下,您必须创建每个Question
对象的副本以实现完全隔离。
你的其他字段没问题,因为它们是原语或对String
的引用(这是不可变的,允许你安全地共享引用)。
答案 1 :(得分:8)
此
this.questionList = incoming.getQuestionList();
最有可能将引用复制到原始列表(我说可能是,因为getQuestionList()
可能会为您提供防御性副本)。您可能需要制作该列表的新副本。也许是包含的Question
个对象。也许他们引用的任何东西。
这是深拷贝的问题。为了可靠地执行此操作,您必须复制所有可变对象。请注意,如果某个对象是 immutable (例如字符串),那么它们就无法更改,因此您可以引用原始文件,确信它们不会被更改。这同样适用于原语。在代码库中鼓励不变性的一个很好的理由。
如果你不能创建一个不可变的类,写你的类,使它成为防御性副本。即,当客户要求收集时,它应该复制并返回。否则你所谓善意的客户可能会改变你的内部状态(无意或无意)。
答案 2 :(得分:5)
创建深层副本时的问题是,除非您在其上使用特定的深层复制构造函数,否则通过引用复制非基本类型的所有内容。
在您的特定情况下,您对bool
,int
或String
变量没有任何问题,因为您按值传递它们(实际上String
通过引用传递但是& #39; s不可变,所以没有问题)但是你传递的是ArrayList<Question> questionList
。当你这样做
this.object = incoming.object
您只需复制参考。所以两个变量都指向内存中的同一个对象,所以你没有深度复制它。您必须创建具有相同内部值的对象的另一个实例,然后您将确定,例如this.object = new YourObject(incoming.object)
。
请注意,通常意味着你的课程在作曲树中更复杂,你需要更多地深入了解变量,直到你复制它们为止。
答案 3 :(得分:0)
如果我们需要复制一个简单的pojo(不嵌套)。那么浅拷贝就足够了。
克隆类
import 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);