用Java克隆子类

时间:2010-12-01 21:06:14

标签: java clone subclass

我需要在Java中克隆一个子类,但是在发生这种情况的代码中,我不会知道子类类型,只知道超类。这样做的最佳设计模式是什么?

示例:

class Foo {
    String myFoo;
    public Foo(){}
    public Foo(Foo old) {
        this.myFoo = old.myFoo;
    }
}

class Bar extends Foo {
    String myBar;
    public Bar(){}
    public Bar(Bar old) {
        super(old); // copies myFoo
        this.myBar = old.myBar;
    }
}

class Copier {
    Foo foo;

    public Foo makeCopy(Foo oldFoo) {

        // this doesn't work if oldFoo is actually an
        // instance of Bar, because myBar is not copied
        Foo newFoo = new Foo(oldFoo);
        return newFoo;

        // unfortunately, I can't predict what oldFoo's the actual class
        // is, so I can't go:
        // if (oldFoo instanceof Bar) { // copy Bar here }
    }
}

7 个答案:

答案 0 :(得分:6)

如果您可以控制要复制的类,那么虚拟方法就是前进的方法:

class Foo {
    ...
    public Foo copy() {
        return new Foo(this);
    }
}
class Bar extends Foo {
    ...
    @Override public Bar copy() {
        return new Bar(this);
    }
}

(理想情况下,要使课程抽象或有效最终。)

答案 1 :(得分:3)

Java中最标准的方法是使每个可克隆的类实现Cloneable,并使用适用于该类的克隆的公共版本覆盖Object.clone()(默认情况下{{ 1}}制作对象的浅表副本。)

请注意,许多人认为Object.clone() / Cloneable是糟糕的设计。

答案 2 :(得分:2)

如果以正确方式实现clone / clonable,则不会出现任何超类问题。这是因为Object.clone()是一个非常特殊的方法 - 在一个非常简洁的描述中:Object.clone()将始终返回与其调用的类型相同的对象。

@see http://download.oracle.com/javase/6/docs/api/java/lang/Object.html#clone%28%29

因此克隆和克隆的正确实现将是:

class Foo implements Clonable{
    String myFoo;
    ...
    Foo clone() {
      try {
        Foo clone = (Foo) super.clone();
        clone.myFoo = this.myFoo;
        return clone;
      } catch (CloneNotSupportedException e) {
        throw new RuntimeException("this could never happen", e);
      }
    }
}

class Bar extends Foo {
    String myBar;
    ...
    Bar clone() {
      try {
        Bar clone = (Bar) super.clone();
        clone.myBar = this.myBar();
        return clone;
      } catch (CloneNotSupportedException e) {
        throw new RuntimeException("this could never happen", e);
      }
    }
}

然后复印机的实施很简单:

class Copier {
  Foo foo;

  /**
   * @return return a Foo if oldFoo is of class Foo, return Bar if oldClass is of class Bar.
   */
  public Foo makeCopy(Foo oldFoo) {
    return oldFoo.clone();
  }
}

答案 3 :(得分:0)

执行此操作的方法是在newInstance()Foo中创建方法Bar。这两个实现都可以创建一个正确类型的对象并返回它,复制代码只需要知道它必须使用oldFoo.newInstance()来获取该对象的副本。

答案 4 :(得分:0)

如果需要深度克隆对象,最好的方法是使用Java序列化。这要求该对象实现Serializable,但它将创建一个全新的克隆对象,并且没有对原始对象的共享引用。

您可以让您的主类实现Serializable,因此每个子类都会自动支持它。

最后,这可能会更新为使用Piped Streams而不是ByteArrayOutputStream来使用更少的内存并且速度更快,但如果您有小对象则不会注意到它。

public static<T> T clone(T object) {
    try {
        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bOut);
        out.writeObject(object);
        out.close();

        ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray()));
        T copy = (T)in.readObject();
        in.close();

        return copy;
    }
    catch(Exception e) {
        throw new RuntimeException(e);
    }
}

答案 5 :(得分:0)

我不确定这是否完全符合您的要求,但我建议您查看Factory pattern

答案 6 :(得分:0)

此模式/解决方案使用复制构造函数和重写的类型化虚函数的组合,并将实例传播到每个超类。

public class A {
    private String myA;

    public A(A a) {
        myA = a.myA;
    }

    public A copy() {
        return new A(this);
    }
}

public class B extends A {
    private String myB;

    public B(B b) {
        super(b);
        myB = b.myB;
    }

    public B copy() {
        return new B(this);
    }
}

public class C extends B {
    private String myC;

    public C(C c) {
        super(c);
        this.myC = c.myC;
    }

    public C copy() {
        return new C(this);
    }
}