我正在尝试在java中实现一个复制构造函数。我面临着类的非原始类型字段的问题。在创建新副本时,它正在共享成员。例如
public class Bad implements Cloneable {
private ArrayList<Integer> a;
private Object c;
public static void main(String[] args) {
Bad b1 = new Bad();
b1.a.add(10);
System.out.println(b1.a);
Bad b2 = b1.clone();
b2.a.add(12);
System.out.println(b1.a);
}
Bad() {
a = new ArrayList<>();
c = null;
}
Bad(Bad b) {
a = b.a;
c = b.c;
}
public Bad clone() {
return new Bad(this);
}
}
结果是:
[10]
[10, 12]
我不希望这种情况发生。以此为例。我原来的问题包括更多用户定义的字段 或者有没有图书馆为我工作?提前谢谢。
答案 0 :(得分:3)
复制构造函数的简单规则:
复制一个只包含具有原始值和不可变值的字段的对象是一种简单的模式。
复制其字段包含可变对象的对象可能会使该过程变得极其昂贵,具体取决于可变对象的复杂程度(想象一个包含其值也是Maps的Map的ArrayList)。但是,如果您希望获得安全副本,则制作可变字段的新副本是必不可少的。
答案 1 :(得分:2)
整数是不可变的,但你需要创建一个全新的 ArrayList ,我的意思是:
Bad(Bad b) {
a = b.a;
c = b.c;
}
改为
Bad(Bad b) {
a = new ArrayList<>(b.a);
c = // this here must be copy constructed too
}
然后你会得到
[10]
[10]
答案 2 :(得分:1)
正确的方法是创建列表的新实例,而不是将引用传递给原始列表。
Bad(Bad b) {
a = new ArrayList<>(b.a);
c = b.c; // this should call clone or something similar as well
}
另请注意,如果你在ba列表中有一些非原始类型,那么你也必须复制/克隆所有子元素(现在不需要它,因为你在其中有Integer,这是不可变的。)