3

我有一个类(“管理器”),它管理一个对象集合,这些对象都植根于一个共同的超类(“管理”)。manager 类有时需要复制选定的托管对象,但无法知道它是 Managed 的​​哪个子类。在我看来,最好的(如果不是唯一的)方法是使用 Cloneable。然后,对于我需要复制的任何托管对象,我调用 managedObject.clone()。当然,它必须正确实施。我已经阅读了许多关于“只使用复制构造函数”或为所有子类实现 myManagedSubClass.copy() 方法的警告。我看不到如何使用“真正的”复制构造函数,因为我需要知道类型:

ManagedSubclass copiedObject = new ManagedSubclass(existingManagedSubclassObject);

如果我实现 copy() 方法,我认为应该是这样的:

class Managed {
  public Managed copy() {
    Managed newObject = new Managed(Managed other);
    // fixup mutable fields in newObject
  }
}

但在我的使用中,我必须将返回值转换为预期的类型。如果我忘记在所有托管子类上实现 copy(),那么我最终会将超类强制转换为子类类型。我无法在 Managed 上设置受版权保护的可见性,因为这是直接复制的有效类。即使不是这种情况,我也必须在每个可以复制的子类上实现复制,并使用所有机制来处理可变字段的深层副本,或者建立我自己的某个通用名称的受保护方法的协议来处理所有问题该级别的超类引入的可变字段。

似乎尽管对 Cloneable 普遍感到愤怒和仇恨,但它是做我想做的最好的方式。我错过了什么吗?

4

3 回答 3

2

在正确的时间使用正确的工具。

如果您需要Cloneable,请使用它。但是要知道它拥有的所有流量。

clone()名声不好,因为它对于它所做的事情和做得不好的事情来说太复杂了。除非您有最终字段或零参数构造函数调用另一个构造函数,否则只要按照建议实现它就可以使用它。

于 2015-11-17T00:38:32.717 回答
2

我更喜欢使用复制构造函数来复制可变对象。在编写构造函数时,您必须调用super(...),在这里您可以使用超类的复制构造函数。这种调用超类的构造函数然后分配当前类的字段的方法类似于您编写clone方法的方式(如果需要,调用super.clone()然后重新分配字段)。这样做的一个优点clone是您永远不必使用无用的try {...} catch (CloneNotSupportedException e) {}结构。复制构造函数相对于使用的另一个优点clone是您可以制作可变字段final,而clone需要您在super.clone()使用原始副本调用后重新分配字段。

编写方法时不能使用继承,copy因为super.copy()返回超类的实例。但是,如果您喜欢使用方法而不是构造函数的想法,您可以copy在复制构造函数之外提供一个方法。

这是一个例子。

interface Copyable {
    Copyable copy();
}

class ImplA implements Copyable {

    private String field;

    public ImplA(ImplA implA) {
        this.field = implA.field;
    }

    @Override
    public ImplA copy() {
        return new ImplA(this);
    }

    // other constructors and methods that mutate state.
}

class ImplB extends ImplA {

    private int value;
    private final List<String> list;  // This field could not be final if we used clone.

    public ImplB(ImplB implB) {
        super(implB);                 // Here we invoke the copy constructor of the super class.
        this.value = implB.value;
        this.list = new ArrayList<>(implB.list);
    }

    @Override
    public final ImplB copy() {
        return new ImplB(this);
    }

    // other constructors and methods that mutate state.
}
于 2015-11-17T01:05:57.597 回答
0

Clone 的强大之处在于它与继承一起工作的运行时动态行为,而复制构造函数不适合这种行为。

使对象可克隆的标准方法是:

  1. 实现可克隆
  2. 覆盖clone()并公开
  3. clone(),调用super.clone() Managed obj = (Managed) super.clone();
  4. 对象的克隆将对所有字段进行简单的内存复制。这适用于原语和对不可变对象的引用。对于可变对象,您需要根据需要克隆/复制它们

如果Managed曾经被继承,并且子类正确实现了克隆,那么克隆它将返回正确的类型。例如

Managed m = new SubTypeOfManaged();
m.clone(); // returns a cloned SubTypeOfManaged
于 2015-11-17T00:36:44.780 回答