2017-08-31 97 views
1

我想在java中实现复制构造函数。我正面临类的非原始类型字段的问题。在创建新副本时,它正在共享成员。例在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

尝试'a = new ArrayList <>(b.a);'这应该使用b中的内容创建一个新的arrayList。一个' – Lino

回答

2

Integer是不可改变的,但你需要创建一个全新的的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]

+1

实际参数有'整数'类型,而该字段仍然是一个'对象'。你必须找到一个应对机制,以及为名单 – Andrew

+0

好!谢谢! –

1

正确的方法是创建列表的新实例,而不是将引用传递给原始列表。

Bad(Bad b) { 
    a = new ArrayList<>(b.a); 
    c = b.c; // this should call clone or something similar as well 
} 

还要注意的是,如果你将有BA列表内的一些非原始类型,那么你就必须复制/克隆所有子元素,以及(这是不是现在需要你有整型在它不可变的情况下)。

3

用于复制构造简单的规则:

  • 原始值可以被复制原样;它们只是没有单独标识的值
  • 对不可变类型引用(例如String,Integer,任何枚举类常量)也可以原样复制;虽然原始和复制的对象将共享相同的引用,但引用的对象是不可变的,并且将永远不会改变
  • 引用可变类型(例如Date,ArrayList,任何数组)必须被复制到一个新的实例类型;否则原件和复制的对象将共享相同的可变领域对象的引用(这是不是你想要的)

制作只包含有原始的和不可改变的值的字段对象的副本是很容易的模式。

复制一个其字段包含可变对象的对象可能会导致该过程艰巨且昂贵,这取决于可变对象的复杂程度(想象一个包含其值也是地图的Map的ArrayList)。但是,如果您希望获得安全副本,则制作可变字段的新副本至关重要。