复制构造函数而不是 Java 中的克隆

Copy constructors instead of Clone in java

提问人:aravindkanna 提问时间:8/31/2017 最后编辑:aravindkanna 更新时间:8/31/2017 访问量:1958

问:

我正在尝试在 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]

我不希望这种情况发生。以此为例。我原来的问题包括更多用户定义的字段。
或者有没有图书馆为我做这项工作?提前致谢。

Java 引用 克隆 复制构造函数

评论

0赞 Lino 8/31/2017
尝试这应该创建一个新的 arrayList,其中包含来自a = new ArrayList<>(b.a);b.a

答:

2赞 ΦXocę 웃 Пepeúpa ツ 8/31/2017 #1

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 Tobilko 8/31/2017
实际参数具有 type,而字段仍然是 .你必须找到一个应对机制,以及清单IntegerObject
1赞 zdenda.online 8/31/2017 #2

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

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

还要注意的是,如果你在 b.a 列表中有一些非原始类型,那么你也必须复制/克隆所有子元素(现在不需要它,因为你里面有不可变的整数)。

3赞 scottb 8/31/2017 #3

复制构造函数的简单规则:

  • 基元值可以按原样复制;它们只是没有单独身份的价值观
  • 对不可变类型的引用(例如。String、Integer、任何枚举类常量)也可以按原样复制;尽管原始对象和复制对象将共享相同的引用,但引用的对象是不可变的,并且永远不会更改
  • 对可变类型的引用(例如。Date、ArrayList、any array) 必须复制到该类型的新实例中;否则,原始对象和复制对象将共享对同一可变字段对象的引用(这不是您想要的)

创建仅包含具有基元值和不可变值的字段的对象的副本是简单模式。

复制字段包含可变对象的对象可能会使该过程变得艰巨且昂贵,具体取决于可变对象的复杂程度(假设一个 ArrayList 包含一个值也是 Map 的 Map)。但是,如果您希望拥有一个安全的副本,则必须创建可变字段的新副本。